/*
 Navicat Premium Data Transfer

 Source Server         : 39.106.107.5
 Source Server Type    : MySQL
 Source Server Version : 50173
 Source Host           : 39.106.107.5
 Source Database       : jblog

 Target Server Type    : MySQL
 Target Server Version : 50173
 File Encoding         : utf-8

 Date: 04/15/2019 22:54:56 PM
*/

SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
--  Table structure for `article`
-- ----------------------------
DROP TABLE IF EXISTS `article`;
CREATE TABLE `article` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `categoryId` int(11) NOT NULL COMMENT '分类Id',
  `title` varchar(40) NOT NULL COMMENT '标题',
  `content` longtext NOT NULL COMMENT '内容',
  `description` varchar(500) NOT NULL COMMENT '文章简介  用于列表显示',
  `status` int(11) NOT NULL DEFAULT '0' COMMENT '状态 0：正常  1：不可用',
  `author` varchar(15) DEFAULT 'Coriger' COMMENT '作者',
  `createTime` datetime NOT NULL COMMENT '发表时间',
  `updateTime` datetime DEFAULT NULL COMMENT '发表时间',
  `showCount` int(11) NOT NULL DEFAULT '0' COMMENT '浏览量',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=259116 DEFAULT CHARSET=utf8 COMMENT='文章表';

-- ----------------------------
--  Records of `article`
-- ----------------------------
BEGIN;
INSERT INTO `article` VALUES ('251655', '10001', '微服务中 Dubbo 和 Spring Cloud 架构技术路线对比', '微服务中 Dubbo 和 Spring Cloud 架构技术路线对比\n\n微服务主要的优势如下：\n1、降低复杂度\n将原来偶合在一起的复杂业务拆分为单个服务，规避了原本复杂度无止境的积累。每一个微服务专注于单一功能，并通过定义良好的接口清晰表述服务边界。每个服务开发者只专注服务本身，通过使用缓存、DAL等各种技术手段来提升系统的性能，而对于消费方来说完全透明。\n2、可独立部署\n由于微服务具备独立的运行进程，所以每个微服务可以独立部署。当业务迭代时只需要发布相关服务的迭代即可，降低了测试的工作量同时也降低了服务发布的风险。\n3、容错\n在微服务架构下，当某一组件发生故障时，故障会被隔离在单个服务中。 通过限流、熔断等方式降低错误导致的危害，保障核心业务正常运行。\n4、扩展\n单块架构应用也可以实现横向扩展，就是将整个应用完整的复制到不同的节点。当应用的不同组件在扩展需求上存在差异时，微服务架构便体现出其灵活性，因为每个服务可以根据实际需求独立进行扩展。\n本文主要围绕微服务的技术选型、通讯协议、服务依赖模式、开始模式、运行模式等几方面来综合比较Dubbo和Spring Cloud 这2种开发框架。\n一、核心部件\n微服务的核心要素在于服务的发现、注册、路由、熔断、降级、分布式配置，基于上述几种必要条件对Dubbo和Spring Cloud做出对比。\n1、总体架构\nDubbo 核心部件:\nProvider： 暴露服务的提供方，可以通过jar或者容器的方式启动服务\nConsumer：调用远程服务的服务消费方。\nRegistry： 服务注册中心和发现中心。\nMonitor： 统计服务和调用次数，调用时间监控中心。（dubbo的控制台页面中可以显示，目前只有一个简单版本）\nContainer：服务运行的容器。\n▲Dubbo 总体架构\nSpring Cloud总体架构:\nService Provider： 暴露服务的提供方。\nService Consumer：调用远程服务的服务消费方。\nEureKa Server： 服务注册中心和服务发现中心。\n▲Spring Cloud总体架构\n点评：从整体架构上来看，二者模式接近，都需要需要服务提供方，注册中心，服务消费方。\n2、微服务架构核心要素\nDubbo只是实现了服务治理，而Spring Cloud子项目分别覆盖了微服务架构下的众多部件，而服务治理只是其中的一个方面。Dubbo提供了各种Filter，对于上述中“无”的要素，可以通过扩展Filter来完善。\n例如\n1．分布式配置：可以使用淘宝的diamond、百度的disconf来实现分布式配置管理\n2．服务跟踪：可以使用京东开源的Hydra，或者扩展Filter用Zippin来做服务跟踪\n3．批量任务：可以使用当当开源的Elastic-Job、tbschedule\n点评：从核心要素来看，Spring Cloud 更胜一筹，在开发过程中只要整合Spring Cloud的子项目就可以顺利的完成各种组件的融合，而Dubbo缺需要通过实现各种Filter来做定制，开发成本以及技术难度略高。\n二、通讯协议\n基于通讯协议层面对2种框架支持的协议类型以及运行效率方面进行比较；\n（一）、支持协议\n1、Dubbo：dubbo使用RPC通讯协议，提供序列化方式如下：\ndubbo：Dubbo缺省协议采用单一长连接和NIO异步通讯，适合于小数据量大并发的服务调用，以及服务消费者机器数远大于服务提供者机器数的情况\nrmi：RMI协议采用JDK标准的java.rmi.*实现，采用阻塞式短连接和JDK标准序列化方式\nHessian:Hessian协议用于集成Hessian的服务，Hessian底层采用Http通讯，采用Servlet暴露服务，Dubbo缺省内嵌Jetty作为服务器实现\nhttp:采用Spring的HttpInvoker实现\nWebservice:基于CXF的frontend-simple和transports-http实现\n2、Spring Cloud：Spring Cloud 使用HTTP协议的REST API\n（二）、性能比较\n使用一个Pojo对象包含10个属性，请求10万次，Dubbo和Spring Cloud在不同的线程数量下，每次请求耗时（ms）如下：\n说明：客户端和服务端配置均采用阿里云的ECS服务器，4核8G配置，dubbo采用默认的dubbo协议\n点评：dubbo支持各种通信协议，而且消费方和服务方使用长链接方式交互，通信速度上略胜Spring Cloud，如果对于系统的响应时间有严格要求，长链接更合适。\n三、服务依赖方式\nDubbo：服务提供方与消费方通过接口的方式依赖，服务调用设计如下：\ninterface层：服务接口层，定义了服务对外提供的所有接口\nMolel层：服务的DTO对象层，\nbusiness层：业务实现层，实现interface接口并且和DB交互\n因此需要为每个微服务定义了各自的interface接口，并通过持续集成发布到私有仓库中，调用方应用对微服务提供的抽象接口存在强依赖关系，开发、测试、集成环境都需要严格的管理版本依赖。\n通过maven的install & deploy命令把interface和Model层发布到仓库中，服务调用方只需要依赖interface和model层即可。在开发调试阶段只发布Snapshot版本。等到服务调试完成再发布Release版本，通过版本号来区分每次迭代的版本。通过xml配置方式即可方面接入dubbo，对程序无入侵。\n▲Dubbo接口依赖方式\nSpring Cloud：服务提供方和服务消费方通过json方式交互，因此只需要定义好相关json字段即可，消费方和提供方无接口依赖。通过注解方式来实现服务配置，对于程序有一定入侵。\n点评：Dubbo服务依赖略重，需要有完善的版本管理机制，但是程序入侵少。而Spring Cloud通过Json交互，省略了版本管理的问题，但是具体字段含义需要统一管理，自身Rest API方式交互，为跨平台调用奠定了基础。\n四、组件运行流程\n下图中的每个组件都是需要部署在单独的服务器上，gateway用来接受前端请求、聚合服务，并批量调用后台原子服务。每个service层和单独的DB交互。\n▲Dubbo组件运行流程\ngateWay:前置网关，具体业务操作，gateWay通过dubbo提供的负载均衡机制自动完成\nService：原子服务，只提供该业务相关的原子服务\nZookeeper：原子服务注册到zk上\n▲Spring Cloud 组件运行\nSpring Cloud\n所有请求都统一通过 API 网关（Zuul）来访问内部服务。\n网关接收到请求后，从注册中心（Eureka）获取可用服务。\n由 Ribbon 进行均衡负载后，分发到后端的具体实例。\n微服务之间通过 Feign 进行通信处理业务。\n点评：业务部署方式相同，都需要前置一个网关来隔绝外部直接调用原子服务的风险。Dubbo需要自己开发一套API 网关，而Spring Cloud则可以通过Zuul配置即可完成网关定制。使用方式上Spring Cloud略胜一筹。\n五、微服务架构组成以及注意事项\n到底使用是dubbo还是Spring Cloud其实并不重要，重点在于如何合理的利用微服务。下面是一张互联网通用的架构图,其中每个环节都是微服务的核心部分。\n（一）、架构分解\n网关集群：数据的聚合、实现对接入客户端的身份认证、防报文重放与防数据篡改、功能调用的业务鉴权、响应数据的脱敏、流量与并发控制等\n业务集群：一般情况下移动端访问和浏览器访问的网关需要隔离，防止业务耦合\nLocal Cache：由于客户端访问业务可能需要调用多个服务聚合，所以本地缓存有效的降低了服务调用的频次，同时也提示了访问速度。本地缓存一般使用自动过期方式，业务场景中允许有一定的数据延时。\n服务层：原子服务层，实现基础的增删改查功能，如果需要依赖其他服务需要在Service层主动调用\nRemote Cache：访问DB前置一层分布式缓存，减少DB交互次数，提升系统的TPS\nDAL：数据访问层，如果单表数据量过大则需要通过DAL层做数据的分库分表处理。\nMQ：消息队列用来解耦服务之间的依赖，异步调用可以通过MQ的方式来执行\n数据库主从：服务化过程中毕竟的阶段，用来提升系统的TPS\n（二）注意事项\n服务启动方式建议使用jar方式启动，启动速度快，更容易监控\n缓存、缓存、缓存，系统中能使用缓存的地方尽量使用缓存，通过合理的使用缓存可以有效的提高系统的TPS\n服务拆分要合理，尽量避免因服务拆分而导致的服务循环依赖\n合理的设置线程池，避免设置过大或者过小导致系统异常', '微服务中 Dubbo 和 Spring Cloud 架构技术路线对比', '1', 'Coriger', '2018-05-21 22:13:38', null, '228'), ('251739', '10001', '乐观锁与悲观锁', '乐观锁与悲观锁\n\n乐观锁(OptimisticLock)\n每次取数据的时候都认为别人不会修改，所以不会上锁，在更新的时候会判断一下在此期间别人有没有去更新这个数据，使用版本号等机制。\n\n乐观锁实现方式：\n\n1.使用自增长的整数表示数据版本号。更新时检查版本号是否一致。\n\n2.使用时间戳来实现.\n\n3.CAS操作方式：即compare and swap 或者 compare and set，涉及到三个操作数，数据所在的内存值，预期值，新值。当需要更新时，判断当前内存值与之前取到的值是否相等，若相等，则用新值更新，若失败则重试，一般情况下是一个自旋操作，即不断的重试。\n\n乐观锁代码示例：\n\nif (checkResourceVersion) { // 检查资源版本是否一致\n\n// do something\n\n}else {\n\n// 过期， 更新数据无效\n\n}\n\n悲观锁(PessimisticLock)\n每次取数据的时候认为别人会修改，所以每次在拿数据的时候都会上锁，别人想拿这个数据就会block直到它拿到锁。\n\nDB的行锁、表锁等，适用于数据一致性比较高的场景。\n\n悲观锁代码示例：\n\nwhile (!lock.tryGet) { // 一直等待获取锁权限\n\n// do something，会经历等待锁、获取锁、释放锁的过程，是比较占用资源的，但是确保了资源的并发访问可能出现的问题。\n\nlock.release\n\nbreak\n\n}\n\n\n', '乐观锁与悲观锁', '1', 'Coriger', '2018-05-21 22:53:23', null, '227'), ('251790', '10001', '互联网高并发架构的8种设计模式演化', '1、单库单应用模式\n这种是最简单的模式，即一个数据一个应用服务器，一般在产品发布初期使用会比较方便，单日30万到50万PV以下一般没有问题。\n2、内容分发模式\n在主机中使用了静态文件缓存之后，还可以使用CDN的方式把静态文件分发到离用户最近的节点上以达到快速响应的目的，一般在百万级别的PV时需要使用。\n3、查询分离模式\n主要是指数据库的读写分离，能够降低响应延时，在千万级别的PV时会使用。\n4、微服务模式\n微服务就是把一个单应用拆分成多个服务，每个服务部署在各自的主机中，最后通过一个ESB来管理和调度这些服务，优点是各司其职不会出现混乱和一致性瘫痪，缺点也很明显，就是集成测试和协同发布难度大增。\n5、分库分表模式\n当一个表的数据出现上亿级别的时候就要考虑分表了，比如订单数据等，根据用户的属性或者时间来拆分成多个表存储，甚至是拆分成多个库存储。\n6、多级缓存模式\n可以把数据缓存到redis、memcache或者分布式文件系统之中，一般是在500万PV之上需要使用。\n7、弹性伸缩模式\n当应用容易出现波峰波谷的情况时使用弹性伸缩模式可以有效降低硬件资源的成本，特别是在使用公有云的时候这个成本的下降积累会是一个比较大的值。\n8、多机房模式\n如果是自建机房可以在南北方各地安置机房以达到有效降低延迟，以及防止同时宕机的可能性出现。如果是使用公有云可以采用多个节点部署。另外一方面，采用CDN的也是多机房的一种实际应用。\n\n\n\n\n', '互联网高并发架构的8种设计模式演化', '1', 'Coriger', '2018-05-26 15:21:58', null, '304'), ('251827', '10000', '手把手教你阿里云服务器CentOS挂载磁盘流程', '当你购买了一台阿里云ECS服务器的时候，拥有多个磁盘空间就需要进行挂载操作，把数据保存到另一个盘，需要把数据库存储盘进行挂载。\n下面是操作教程，希望对服务器使用的新来同学有所帮助。\n\n提前声明，本文中的磁盘/dev/xvdb 为作者测试服务器上的命名，在您的服务器中可能是 /dev/sdb /dev/vdb 等等，请按您的磁盘名称修改。\n创建挂载目录\nmkdir -p /www\n1、确认是否有没有分区的磁盘,如下图，没有分区的磁盘是 /dev/xvdb ,在您的服务器中可能是 /dev/vdb 请注意按实际名称修改\nfdisk -l\n\n2、为磁盘分区，若已分区的，请跳过！\nfdisk /dev/xvdb\n\n3、输入n开始创建分区\n\n4、输入p创建主分区\n\n5、选择分区号，这里输入1\n\n6、输入分区开始位置，直接回车\n\n7、输入分区结束位置，直接回车\n\n8、输入wq 保存退出\n\n9、检查是否分区成功\nfdisk -l\n\n10、格式化分区，这里请输入你看到的磁盘加分区号，如下图，已格式化过的，请跳过\nmkfs.ext4 /dev/xvdb1\n\n11、将分区挂载信息添加到开启动挂载\necho \"/dev/xvdb1 /www ext4 defaults 0 0\" >> /etc/fstab\n\n12、重新挂载所有分区\nmount -a\n\n13、检查是否挂载成功\ndf\n\n操作到这一步的时候，挂载磁盘已经完成了，你接下来就是进行环境安装和使用了。', '手把手教你阿里云服务器CentOS挂载磁盘流程', '1', 'Coriger', '2017-07-11 16:44:26', '2017-07-11 16:45:21', '104'), ('252036', '10001', '系统幂等性设计与实践', '- **幂等性的定义**\n就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的，不会因为多次点击而产生了副作用。举个最简单的例子，那就是支付，用户购买商品使用支付，支付扣款成功，但是返回结果的时候网络异常，此时钱已经扣了，用户再次点击按钮，此时会进行第二次扣款，返回结果成功，用户查询余额返发现多扣钱了，流水记录也变成了两条。\n\n- **确定需要幂等性的范围**\n`确定大范围`\n		1.	请求层面：读请求、写请求\n					o	其中读请求没有影响数据变化不需要做幂等性\n		2.	微服务层面：负载均衡、api网关、业务逻辑层、数据访问层\n					o	其中负载均衡、api网关、业务逻辑层没有影响数据变化不需要做幂等性\n		•	总结：上面的范围里只有数据访问层和写请求\n`数据访问层-写请求`\n		1.	Insert\n					o	需要做幂等性\n		2.	Update\n					o	直接更新某个值的：不需要做幂等性\n					o	累加操作等计算式的更新：需要做幂等性\n		3.	Delete\n					o	重复删除结果是一样：不需要做幂等性\n\n- **幂等性解决方案**\n		•	没有最优的方案只有最适合的，因为这个和业务的逻辑强相关，所以就简单列举通用的方案\n`Insert幂等方案`\n		1.	数据字段增加唯一索引\n					o	优点：实现简单方便\n					o	缺点：影响数据库性能不适合该字段会被频繁更新的场景，唯一索引比普通索引在写操作上开销会大很多\n		2.	insert时使用临时表查询判断\n					insert into sys_user(name,password)\n					select \'admin\', \'123456\' from dual\n					where not exists(select 1 from sys_user where name=\'admin\');\n					o	优点：不需要创建唯一索引，语法相对通用(mysql和oracle)\n					o	缺点：写操作会增加一次select子查询开销，增加sql语法的复杂度可读性较差\n		3.	细粒度分布式锁+select + insert\n					•	意思就是先加一个细粒度的分布式锁，然后select查一下是否存在，不存在再insert\n					•	方法级幂等性\n							优点：性能影响较少，使用的是细粒度锁，所以只有重复提交记录时才会阻塞\n							缺点：写操作会增加一次select开销，实现难度相对较大因为需要分布式细粒度锁\n`Update计算操作幂等方案`\n		•	这个需要结合具体业务来设计方案，常用的场景可通过版本号的方式来控制\n					1 . 在表里面添加version字段\n							alter table sys_user add version int default 0;\n					2.	然后更新的时候通过这个version来判断是否为过期无效操作，这是乐观锁的一种思路\n							update sys_user set age=age+1, version=version+1 where version=xx\n\n', '系统幂等性设计与实践', '1', 'Coriger', '2019-04-15 22:17:42', null, '2'), ('252981', '10000', 'Redis入侵特征', '#Redis入侵特征：\n  1.Redis可能执行过FLUSHALL方法，整个Redis数据库被清空\n  2.在Redis数据库中新建了一个名为crackit（网上流传的命令指令）的键值对，内容为一个SSH公钥。\n  3./ is\n解决方案：\n1.禁止一些高危命令\n   修改redis.conf文件，添加\n      rename-command FLUSHALL \"\"\n      rename-command CONFIG   \"\"\n      rename-command EVAL     \"\"\n   来禁用修改DB文件地址\n以下权限运行Redis服务\n   为Redis服务创建单独的用户和家目录，并且配置禁止登陆\n为Redis添加密码验证\n  修改redis.conf文件，添加\n  requirepass mypassword\n禁止外网访问Redis\n  修改redis.conf文件，添加或修改\n   bind 127.0.0.1\n使得Redis服务只在当前主机可用\n\n扫描工具：\n###以Ubuntu为例\nsu\n\n#### Requirements\napt-get install redis-server expect zmap\n\ngit clone https://github.com/qingxp9/yyfexploit\ncd yyfexploit/redis\n\n#### 扫描6379端口\n####如果你要扫内网，把/etc/zmap/zmap.conf中blacklist-file这一行注释掉\nzmap -p 6379 10.0.0.0/8 -B 10M -o ip.txt\n\n#### Usage\n./redis.sh ip.txt\n\n将会生成几个txt文件记录结果其中：runasroot.txt表示redis无认证，\n且以root运行noauth.txt表示redis无认证，但以普通用户运行rootshell.txt已写入公钥，\n可直接以证书登录根用户\nssh -i id_rsa root @ xxxx\n', 'Redis入侵特征', '1', 'Coriger', '2017-08-25 15:41:33', null, '266'), ('253104', '10001', '后端架构师技术图谱', '<h1>后端架构师技术图谱</h1>\n\n* [数据结构](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#数据结构)\n	* [队列](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#队列)\n	* [集合](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#集合)\n	* [链表、数组](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#链表数组)\n	* [字典、关联数组](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#字典关联数组)\n	* [栈](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#栈)\n	* [树](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#树)\n		* [二叉树](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#二叉树)\n		* [完全二叉树](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#完全二叉树)\n		* [平衡二叉树](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#平衡二叉树)\n		* [二叉查找树（BST）](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#二叉查找树bst)\n		* [红黑树](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#红黑树)\n		* [B，B+，B*树](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#b-bb树)\n		* [LSM 树](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#lsm-树)\n	* [BitSet](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#bitset)\n* [常用算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#常用算法)\n	* [排序、查找算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#排序查找算法)\n		* [选择排序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#选择排序)\n		* [冒泡排序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#冒泡排序)\n		* [插入排序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#插入排序)\n		* [快速排序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#快速排序)\n		* [归并排序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#归并排序)\n		* [希尔排序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#希尔排序)\n		* [堆排序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#堆排序)\n		* [计数排序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#计数排序)\n		* [桶排序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#桶排序)\n		* [基数排序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#基数排序)\n		* [二分查找](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#二分查找)\n		* [Java 中的排序工具](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#java-中的排序工具)\n	* [布隆过滤器](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#布隆过滤器)\n	* [字符串比较](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#字符串比较)\n		* [KMP 算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#kmp-算法)\n	* [深度优先、广度优先](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#深度优先广度优先)\n	* [贪心算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#贪心算法)\n	* [回溯算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#回溯算法)\n	* [剪枝算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#剪枝算法)\n	* [动态规划](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#动态规划)\n	* [朴素贝叶斯](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#朴素贝叶斯)\n	* [推荐算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#推荐算法)\n	* [最小生成树算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#最小生成树算法)\n	* [最短路径算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#最短路径算法)\n* [并发](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#并发)\n	* [Java 并发](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#java-并发)\n	* [多线程](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#多线程)\n	* [线程安全](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#线程安全)\n	* [一致性、事务](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#一致性事务)\n		* [事务 ACID 特性](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#事务-acid-特性)\n		* [事务的隔离级别](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#事务的隔离级别)\n		* [MVCC](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#mvcc)\n	* [锁](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#锁)\n		* [Java中的锁和同步类](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#java中的锁和同步类)\n		* [公平锁 &amp; 非公平锁](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#公平锁--非公平锁)\n		* [悲观锁](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#悲观锁)\n		* [乐观锁 &amp; CAS](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#乐观锁--cas)\n		* [ABA 问题](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#aba-问题)\n		* [CopyOnWrite容器](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#copyonwrite容器)\n		* [RingBuffer](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#ringbuffer)\n		* [可重入锁 &amp; 不可重入锁](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#可重入锁--不可重入锁)\n		* [互斥锁 &amp; 共享锁](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#互斥锁--共享锁)\n		* [死锁](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#死锁)\n* [操作系统](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#操作系统)\n	* [计算机原理](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#计算机原理)\n	* [CPU](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#cpu)\n		* [多级缓存](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#多级缓存)\n	* [进程](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#进程)\n	* [线程](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#线程)\n	* [协程](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#协程)\n	* [Linux](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#linux)\n* [设计模式](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#设计模式)\n	* [设计模式的六大原则](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#设计模式的六大原则)\n	* [23种常见设计模式](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#23种常见设计模式)\n	* [应用场景](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#应用场景)\n	* [单例模式](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#单例模式)\n	* [责任链模式](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#责任链模式)\n	* [MVC](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#mvc)\n	* [IOC](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#ioc)\n	* [AOP](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#aop)\n	* [UML](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#uml)\n	* [微服务思想](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#微服务思想)\n		* [康威定律](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#康威定律)\n* [运维 &amp; 统计 &amp; 技术支持](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#运维--统计--技术支持)\n	* [常规监控](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#常规监控)\n	* [APM](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#apm)\n	* [统计分析](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#统计分析)\n	* [持续集成(CI/CD)](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#持续集成cicd)\n		* [Jenkins](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#jenkins)\n		* [环境分离](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#环境分离)\n	* [自动化运维](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#自动化运维)\n		* [Ansible](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#ansible)\n		* [puppet](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#puppet)\n		* [chef](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#chef)\n	* [测试](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#测试)\n		* [TDD 理论](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#tdd-理论)\n		* [单元测试](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#单元测试)\n		* [压力测试](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#压力测试)\n		* [全链路压测](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#全链路压测)\n		* [A/B 、灰度、蓝绿测试](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#ab-灰度蓝绿测试)\n	* [虚拟化](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#虚拟化)\n		* [KVM](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#kvm)\n		* [Xen](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#xen)\n		* [OpenVZ](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#openvz)\n	* [容器技术](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#容器技术)\n		* [Docker](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#docker)\n	* [云技术](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#云技术)\n		* [OpenStack](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#openstack)\n	* [DevOps](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#devops)\n	* [文档管理](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#文档管理)\n* [中间件](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#中间件)\n	* [Web Server](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#web-server)\n		* [Nginx](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#nginx)\n		* [OpenResty](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#openresty)  \n		* [Tengine](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#Tengine)  \n		* [Apache Httpd](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#apache-httpd)\n		* [Tomcat](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#tomcat)\n			* [架构原理](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#架构原理)\n			* [调优方案](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#调优方案)\n		* [Jetty](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#jetty)\n	* [缓存](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#缓存)\n		* [本地缓存](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#本地缓存)\n	* [客户端缓存](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#客户端缓存)\n	* [服务端缓存](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#服务端缓存)\n		* [Web缓存](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#web缓存)\n		* [Memcached](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#memcached)\n		* [Redis](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#redis)\n			* [架构](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#架构)\n			* [回收策略](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#回收策略)\n		* [Tair](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#tair)\n	* [消息队列](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#消息队列)\n		* [消息总线](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#消息总线)\n		* [消息的顺序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#消息的顺序)\n		* [RabbitMQ](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#rabbitmq)\n		* [RocketMQ](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#rocketmq)\n		* [ActiveMQ](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#activemq)\n		* [Kafka](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#kafka)\n		* [Redis 消息推送](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#redis-消息推送)\n		* [ZeroMQ](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#zeromq)\n	* [定时调度](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#定时调度)\n		* [单机定时调度](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#单机定时调度)\n		* [分布式定时调度](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#分布式定时调度)\n	* [RPC](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#rpc)\n		* [Dubbo](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#dubbo)\n		* [Thrift](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#thrift)\n		* [gRPC](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#grpc)\n	* [数据库中间件](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#数据库中间件)\n		* [Sharding Jdbc](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#sharding-jdbc)\n	* [日志系统](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#日志系统)\n		* [日志搜集](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#日志搜集)\n	* [配置中心](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#配置中心)\n	* [API 网关](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#api-网关)\n* [网络](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#网络)\n	* [协议](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#协议)\n		* [OSI 七层协议](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#osi-七层协议)\n		* [TCP/IP](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#tcpip)\n		* [HTTP](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#http)\n		* [HTTP2.0](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#http20)\n		* [HTTPS](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#https)\n	* [网络模型](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#网络模型)\n		* [Epoll](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#epoll)\n		* [Java NIO](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#java-nio)\n		* [kqueue](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#kqueue)\n	* [连接和短连接](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#连接和短连接)\n	* [框架](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#框架)\n	* [零拷贝（Zero-copy）](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#零拷贝zero-copy)\n	* [序列化(二进制协议)](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#序列化二进制协议)\n		* [Hessian](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#hessian)\n		* [Protobuf](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#protobuf)\n* [数据库](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#数据库)\n	* [基础理论](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#基础理论)\n		* [数据库设计的三大范式](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#数据库设计的三大范式)\n	* [MySQL](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#mysql)\n		* [原理](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#原理)\n		* [InnoDB](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#innodb)\n		* [优化](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#优化)\n		* [索引](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#索引)\n			* [聚集索引, 非聚集索引](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#聚集索引-非聚集索引)\n			* [复合索引](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#复合索引)\n			* [自适应哈希索引(AHI)](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#自适应哈希索引ahi)\n		* [explain](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#explain)\n	* [NoSQL](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#nosql)\n		* [MongoDB](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#mongodb)\n		* [Hbase](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#hbase)\n* [搜索引擎](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#搜索引擎)\n	* [搜索引擎原理](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#搜索引擎原理)\n	* [Lucene](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#lucene)\n	* [Elasticsearch](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#elasticsearch)\n	* [Solr](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#solr)\n	* [sphinx](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#sphinx)\n* [性能](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#性能)\n	* [性能优化方法论](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#性能优化方法论)\n	* [容量评估](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#容量评估)\n	* [CDN 网络](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#cdn-网络)\n	* [连接池](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#连接池)\n	* [性能调优](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#性能调优)\n* [大数据](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#大数据)\n	* [流式计算](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#流式计算)\n		* [Storm](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#storm)\n		* [Flink](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#flink)\n		* [Kafka Stream](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#kafka-stream)\n		* [应用场景](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#应用场景-1)\n	* [Hadoop](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#hadoop)\n		* [HDFS](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#hdfs)\n		* [MapReduce](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#mapreduce)\n		* [Yarn](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#yarn)\n	* [Spark](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#spark)\n* [安全](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#安全)\n	* [web 安全](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#web-安全)\n		* [XSS](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#xss)\n		* [CSRF](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#csrf)\n		* [SQL 注入](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#sql-注入)\n		* [Hash Dos](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#hash-dos)\n		* [脚本注入](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#脚本注入)\n		* [漏洞扫描工具](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#漏洞扫描工具)\n		* [验证码](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#验证码)\n	* [DDoS 防范](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#ddos-防范)\n	* [用户隐私信息保护](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#用户隐私信息保护)\n	* [序列化漏洞](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#序列化漏洞)\n	* [加密解密](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#加密解密)\n		* [对称加密](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#对称加密)\n		* [哈希算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#哈希算法)\n		* [非对称加密](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#非对称加密)\n	* [服务器安全](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#服务器安全)\n	* [数据安全](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#数据安全)\n		* [数据备份](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#数据备份)\n	* [网络隔离](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#网络隔离)\n		* [内外网分离](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#内外网分离)\n		* [登录跳板机](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#登录跳板机)\n	* [授权、认证](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#授权认证)\n		* [RBAC](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#rbac)\n		* [OAuth2.0](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#oauth20)\n		* [双因素认证（2FA）](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#双因素认证2fa)\n		* [单点登录(SSO)](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#单点登录sso)\n* [常用开源框架](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#常用开源框架)\n	* [开源协议](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#开源协议)\n	* [日志框架](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#日志框架)\n		* [Log4j、Log4j2](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#log4jlog4j2)\n		* [Logback](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#logback)\n	* [ORM](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#orm)\n	* [网络框架](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#网络框架)\n	* [Web 框架](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#web-框架)\n		* [Spring 家族](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#spring-家族)\n	* [工具框架](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#工具框架)\n* [分布式设计](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#分布式设计)\n	* [扩展性设计](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#扩展性设计)\n	* [稳定性 &amp; 高可用](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#稳定性--高可用)\n		* [硬件负载均衡](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#硬件负载均衡)\n		* [软件负载均衡](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#软件负载均衡)\n		* [限流](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#限流)\n		* [应用层容灾](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#应用层容灾)\n		* [跨机房容灾](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#跨机房容灾)\n		* [容灾演练流程](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#容灾演练流程)\n		* [平滑启动](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#平滑启动)\n	* [数据库扩展](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#数据库扩展)\n		* [读写分离模式](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#读写分离模式)\n		* [分片模式](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#分片模式)\n	* [服务治理](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#服务治理)\n		* [服务注册与发现](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#服务注册与发现)\n		* [服务路由控制](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#服务路由控制)\n	* [分布式一致](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#分布式一致)\n		* [CAP 与 BASE 理论](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#cap-与-base-理论)\n		* [分布式锁](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#分布式锁)\n		* [分布式一致性算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#分布式一致性算法)\n			* [PAXOS](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#paxos)\n			* [Zab](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#zab)\n			* [Raft](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#raft)\n			* [Gossip](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#gossip)\n			* [两阶段提交、多阶段提交](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#两阶段提交多阶段提交)\n		* [幂等](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#幂等)\n		* [分布式一致方案](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#分布式一致方案)\n		* [分布式 Leader 节点选举](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#分布式-leader-节点选举)\n		* [TCC(Try/Confirm/Cancel) 柔性事务](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#tcctryconfirmcancel-柔性事务)\n	* [分布式文件系统](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#分布式文件系统)\n	* [唯一ID 生成](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#唯一id-生成)\n		* [全局唯一ID](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#全局唯一id)\n	* [一致性Hash算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#一致性hash算法)\n* [设计思想 &amp; 开发模式](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#设计思想--开发模式)\n	* [DDD(Domain-driven Design - 领域驱动设计)](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#ddddomain-driven-design---领域驱动设计)\n		* [命令查询职责分离(CQRS)](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#命令查询职责分离cqrs)\n		* [贫血，充血模型](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#贫血充血模型)\n	* [Actor 模式](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#actor-模式)\n	* [响应式编程](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#响应式编程)\n		* [Reactor](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#reactor)\n		* [RxJava](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#rxjava)\n		* [Vert.x](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#vertx)\n	* [DODAF2.0](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#dodaf20)\n	* [Serverless](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#serverless)\n	* [Service Mesh](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#service-mesh)\n* [项目管理](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#项目管理)\n	* [架构评审](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#架构评审)\n	* [重构](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#重构)\n	* [代码规范](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#代码规范)\n	* [代码 Review](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#代码-review)\n	* [RUP](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#rup)\n	* [看板管理](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#看板管理)\n	* [SCRUM](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#scrum)\n	* [敏捷开发](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#敏捷开发)\n	* [极限编程（XP）](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#极限编程xp)\n	* [结对编程](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#结对编程)\n	* [PDCA 循环质量管理](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#pdca-循环质量管理)\n	* [FMEA管理模式](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#fmea管理模式)\n* [通用业务术语](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#通用业务术语)\n* [技术趋势](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#技术趋势)\n* [政策、法规](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#政策法规)\n	* [法律](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#法律)\n		* [严格遵守刑法253法条](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#严格遵守刑法253法条)\n* [架构师素质](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#架构师素质)\n* [团队管理](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#团队管理)\n	* [招聘](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#招聘)\n* [资讯](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#资讯)\n	* [行业资讯](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#行业资讯)\n	* [公众号列表](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#公众号列表)\n	* [博客](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#博客)\n		* [团队博客](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#团队博客)\n		* [个人博客](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#个人博客)\n	* [综合门户、社区](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#综合门户社区)\n	* [问答、讨论类社区](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#问答讨论类社区)\n	* [行业数据分析](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#行业数据分析)\n	* [专项网站](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#专项网站)\n	* [其他类](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#其他类)\n	* [推荐参考书](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#推荐参考书)\n		* [在线电子书](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#在线电子书)\n		* [纸质书](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#纸质书)\n			* [开发方面](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#开发方面)\n			* [架构方面](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#架构方面)\n			* [技术管理方面](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#技术管理方面)\n			* [基础理论](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#基础理论-1)\n			* [工具方面](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#工具方面)\n			* [大数据方面](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#大数据方面)\n* [技术资源](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#技术资源)\n	* [开源资源](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#开源资源)\n	* [手册、文档、教程](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#手册文档教程)\n	* [在线课堂](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#在线课堂)\n	* [会议、活动](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#会议活动)\n	* [常用APP](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#常用app)\n	* [找工作](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#找工作)\n	* [工具](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#工具)\n	* [代码托管](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#代码托管)\n	* [文件服务](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#文件服务)\n	* [综合云服务商](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#综合云服务商)\n		* [VPS](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#vps)\n	\n\n**（Toc generated by [simple-php-github-toc](https://github.com/xingshaocheng/simple-php-github-toc) ）**\n\n# 数据结构\n\n## 队列\n* [《java队列——queue详细分析》](https://www.cnblogs.com/lemon-flm/p/7877898.html)\n	* 非阻塞队列：ConcurrentLinkedQueue(无界线程安全)，采用CAS机制（compareAndSwapObject原子操作）。\n	* 阻塞队列：ArrayBlockingQueue(有界)、LinkedBlockingQueue（无界）、DelayQueue、PriorityBlockingQueue，采用锁机制；使用 ReentrantLock 锁。\n\n* [《LinkedList、ConcurrentLinkedQueue、LinkedBlockingQueue对比分析》](https://www.cnblogs.com/mantu/p/5802393.html)\n\n## 集合\n* [《Java Set集合的详解》](https://blog.csdn.net/qq_33642117/article/details/52040345)\n\n## 链表、数组\n* [《Java集合详解--什么是List》](https://blog.csdn.net/wz249863091/article/details/52853360)\n\n## 字典、关联数组\n* [《Java map 详解 - 用法、遍历、排序、常用API等》](https://baike.xsoftlab.net/view/250.html)\n\n## 栈\n* [《java数据结构与算法之栈（Stack）设计与实现》](https://blog.csdn.net/javazejian/article/details/53362993)\n* [《Java Stack 类》](http://www.runoob.com/java/java-stack-class.html)\n* [《java stack的详细实现分析》](https://blog.csdn.net/f2006116/article/details/51375225)\n	* Stack 是线程安全的。\n	* 内部使用数组保存数据，不够时翻倍。\n\n## 树\n\n### 二叉树\n\n每个节点最多有两个叶子节点。\n*  [《二叉树》](https://blog.csdn.net/cai2016/article/details/52589952)\n\n### 完全二叉树\n* [《完全二叉树》](https://baike.baidu.com/item/%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%8F%89%E6%A0%91/7773232?fr=aladdin)\n	* 叶节点只能出现在最下层和次下层，并且最下面一层的结点都集中在该层最左边的若干位置的二叉树。\n\n### 平衡二叉树\n左右两个子树的高度差的绝对值不超过1，并且左右两个子树都是一棵平衡二叉树。\n* [《浅谈数据结构-平衡二叉树》](http://www.cnblogs.com/polly333/p/4798944.html)\n* [《浅谈算法和数据结构: 八 平衡查找树之2-3树》](http://www.cnblogs.com/yangecnu/p/Introduce-2-3-Search-Tree.html)\n\n### 二叉查找树（BST）\n二叉查找树（Binary Search Tree），也称有序二叉树（ordered binary tree）,排序二叉树（sorted binary tree）。\n\n* [《浅谈算法和数据结构: 七 二叉查找树》](http://www.cnblogs.com/yangecnu/p/Introduce-Binary-Search-Tree.html)\n\n\n### 红黑树\n* [《最容易懂得红黑树》](https://blog.csdn.net/sun_tttt/article/details/65445754)\n	* 添加阶段后，左旋或者右旋从而再次达到平衡。 \n* [《浅谈算法和数据结构: 九 平衡查找树之红黑树》](http://www.cnblogs.com/yangecnu/p/Introduce-Red-Black-Tree.html)\n\n### B，B+，B*树\nMySQL是基于B+树聚集索引组织表\n\n* [《B-树，B+树，B\\*树详解》](https://blog.csdn.net/aqzwss/article/details/53074186)\n* [《B-树，B+树与B\\*树的优缺点比较》](https://blog.csdn.net/bigtree_3721/article/details/73632405)\n	* B+树的叶子节点链表结构相比于 B-树便于扫库，和范围检索。\n### LSM 树\n\nLSM（Log-Structured Merge-Trees）和 B+ 树相比，是牺牲了部分读的性能来换取写的性能(通过批量写入)，实现读写之间的。\nHbase、LevelDB、Tair（Long DB）、nessDB 采用 LSM 树的结构。LSM可以快速建立索引。\n\n* [《LSM树 VS B+树》](https://blog.csdn.net/dbanote/article/details/8897599)\n	* B+ 树读性能好，但由于需要有序结构，当key比较分散时，磁盘寻道频繁，造成写性能。\n	* LSM 是将一个大树拆分成N棵小树，先写到内存（无寻道问题，性能高），在内存中构建一颗有序小树（有序树），随着小树越来越大，内存的小树会flush到磁盘上。当读时，由于不知道数据在哪棵小树上，因此必须遍历（二分查找）所有的小树，但在每颗小树内部数据是有序的。\n	\n* [《LSM树（Log-Structured Merge Tree）存储引擎》](https://blog.csdn.net/u014774781/article/details/52105708)\n	* 极端的说，基于LSM树实现的HBase的写性能比MySQL高了一个数量级，读性能低了一个数量级。\n	* 优化方式：Bloom filter 替代二分查找；compact 小数位大树，提高查询性能。\n	* Hbase 中，内存中达到一定阈值后，整体flush到磁盘上、形成一个文件（B+数），HDFS不支持update操作，所以Hbase做整体flush而不是merge update。flush到磁盘上的小树，定期会合并成一个大树。\n\n## BitSet\n\n经常用于大规模数据的排重检查。\n\n* [《Java Bitset类》](http://www.runoob.com/java/java-bitset-class.html)\n* [《Java BitSet（位集）》](https://blog.csdn.net/caiandyong/article/details/51581160)\n\n# 常用算法\n\n* [《常见排序算法及对应的时间复杂度和空间复杂度》](https://blog.csdn.net/gane_cheng/article/details/52652705)\n\n## 排序、查找算法\n\n* [《常见排序算法及对应的时间复杂度和空间复杂度》](https://blog.csdn.net/gane_cheng/article/details/52652705)\n\n### 选择排序\n* [《Java中的经典算法之选择排序（SelectionSort）》](https://www.cnblogs.com/shen-hua/p/5424059.html)\n	* 每一趟从待排序的记录中选出最小的元素，顺序放在已排好序的序列最后，直到全部记录排序完毕。\n\n### 冒泡排序\n* [《冒泡排序的2种写法》](https://blog.csdn.net/shuaizai88/article/details/73250615)\n	* 相邻元素前后交换、把最大的排到最后。\n	* 时间复杂度 O(n²) \n\n### 插入排序\n* [《排序算法总结之插入排序》](https://www.cnblogs.com/hapjin/p/5517667.html)\n\n### 快速排序\n* [《坐在马桶上看算法：快速排序》](http://developer.51cto.com/art/201403/430986.htm)\n	* 一侧比另外一次都大或小。 \n### 归并排序\n* [《图解排序算法(四)之归并排序》](http://www.cnblogs.com/chengxiao/p/6194356.html)\n	* 分而治之，分成小份排序，在合并(重建一个新空间进行复制)。 \n\n### 希尔排序\nTODO\n\n### 堆排序\n* [《图解排序算法(三)之堆排序》](https://www.cnblogs.com/chengxiao/p/6129630.html)\n	* 排序过程就是构建最大堆的过程，最大堆：每个结点的值都大于或等于其左右孩子结点的值，堆顶元素是最大值。\n\n### 计数排序\n* [《计数排序和桶排序》](https://www.cnblogs.com/suvllian/p/5495780.html)\n	* 和桶排序过程比较像，差别在于桶的数量。\n\n### 桶排序\n* [《【啊哈！算法】最快最简单的排序——桶排序》](http://blog.51cto.com/ahalei/1362789)\n* [《排序算法（三）：计数排序与桶排序》](https://blog.csdn.net/sunjinshengli/article/details/70738527)\n	* 桶排序将[0,1)区间划分为n个相同的大小的子区间，这些子区间被称为桶。\n	* 每个桶单独进行排序，然后再遍历每个桶。\n\n### 基数排序\n\n按照个位、十位、百位、...依次来排。\n\n* [《排序算法系列：基数排序》](https://blog.csdn.net/lemon_tree12138/article/details/51695211)\n* [《基数排序》](https://www.cnblogs.com/skywang12345/p/3603669.html)\n\n\n### 二分查找\n* [《二分查找(java实现)》](https://www.cnblogs.com/coderising/p/5708632.html)\n	* 要求待查找的序列有序。\n	* 时间复杂度 O(logN)。\n\n* [《java实现二分查找-两种方式》](https://blog.csdn.net/maoyuanming0806/article/details/78176957)\n	* while + 递归。\n### Java 中的排序工具\n* [《Arrays.sort和Collections.sort实现原理解析》](https://blog.csdn.net/u011410529/article/details/56668545?locationnum=6&fps=1)\n	* Collections.sort算法调用的是合并排序。\n	* Arrays.sort() 采用了2种排序算法 -- 基本类型数据使用快速排序法，对象数组使用归并排序。\n\n## 布隆过滤器\n\n常用于大数据的排重，比如email，url 等。\n核心原理：将每条数据通过计算产生一个指纹（一个字节或多个字节，但一定比原始数据要少很多），其中每一位都是通过随机计算获得，在将指纹映射到一个大的按位存储的空间中。注意：会有一定的错误率。\n优点：空间和时间效率都很高。\n缺点：随着存入的元素数量增加，误算率随之增加。\n\n* [《布隆过滤器 -- 空间效率很高的数据结构》](https://segmentfault.com/a/1190000002729689)\n* [《大量数据去重：Bitmap和布隆过滤器(Bloom Filter)》](https://blog.csdn.net/zdxiq000/article/details/57626464)\n* [《基于Redis的布隆过滤器的实现》](https://blog.csdn.net/qq_30242609/article/details/71024458)\n	* 基于 Redis 的 Bitmap 数据结构。\n* [《网络爬虫：URL去重策略之布隆过滤器(BloomFilter)的使用》](https://blog.csdn.net/lemon_tree12138/article/details/47973715)\n	* 使用Java中的 BitSet 类 和 加权和hash算法。\n\n## 字符串比较\n\n### KMP 算法\nKMP：Knuth-Morris-Pratt算法（简称KMP）\n核心原理是利用一个“部分匹配表”，跳过已经匹配过的元素。\n* [《字符串匹配的KMP算法》](http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html)\n\n## 深度优先、广度优先\n* [《广度优先搜索BFS和深度优先搜索DFS》](https://www.cnblogs.com/0kk470/p/7555033.html)\n\n## 贪心算法\n* [《算法：贪婪算法基础》](https://www.cnblogs.com/MrSaver/p/8641971.html)\n* [《常见算法及问题场景——贪心算法》](https://blog.csdn.net/a345017062/article/details/52443781)\n\n## 回溯算法\n* [《 五大常用算法之四：回溯法》](https://blog.csdn.net/qfikh/article/details/51960331)\n\n## 剪枝算法\n* [《α-β剪枝算法》](https://blog.csdn.net/luningcsdn/article/details/50930276)\n\n## 动态规划\n* [《详解动态规划——邹博讲动态规划》](https://www.cnblogs.com/little-YTMM/p/5372680.html)\n* [《动态规划算法的个人理解》](https://blog.csdn.net/yao_zi_jie/article/details/54580283)\n\n## 朴素贝叶斯\n\n* [《带你搞懂朴素贝叶斯分类算法》](https://blog.csdn.net/amds123/article/details/70173402)\n	* P(B|A)=P(A|B)P(B)/P(A)\n\n* [《贝叶斯推断及其互联网应用1》](http://www.ruanyifeng.com/blog/2011/08/bayesian_inference_part_one.html)\n* [《贝叶斯推断及其互联网应用2》](http://www.ruanyifeng.com/blog/2011/08/bayesian_inference_part_two.html)\n\n\n## 推荐算法\n* [《推荐算法综述》](http://www.infoq.com/cn/articles/recommendation-algorithm-overview-part01)\n* [《TOP 10 开源的推荐系统简介》](https://www.oschina.net/news/51297/top-10-open-source-recommendation-systems)\n\n## 最小生成树算法\n* [《算法导论--最小生成树（Kruskal和Prim算法）》](https://blog.csdn.net/luoshixian099/article/details/51908175)\n\n## 最短路径算法\n\n* [《Dijkstra算法详解》](https://blog.csdn.net/qq_35644234/article/details/60870719)\n\n# 并发\n\n## Java 并发\n\n* [Java 并发知识合集](https://github.com/CL0610/Java-concurrency)\n* [JAVA并发知识图谱](https://github.com/CL0610/Java-concurrency/blob/master/Java并发知识图谱.png)\n\n## 多线程\n\n* [《40个Java多线程问题总结》](http://www.importnew.com/18459.html)\n\n## 线程安全\n\n* [《Java并发编程——线程安全及解决机制简介》](https://www.cnblogs.com/zhanht/p/5450325.html)\n\n## 一致性、事务\n\n### 事务 ACID 特性\n* [《数据库事务ACID特性》](https://blog.csdn.net/u012440687/article/details/52116108)\n\n### 事务的隔离级别\n\n* 未提交读：一个事务可以读取另一个未提交的数据，容易出现脏读的情况。\n* 读提交：一个事务等另外一个事务提交之后才可以读取数据，但会出现不可重复读的情况（多次读取的数据不一致），读取过程中出现UPDATE操作，会多。（大多数数据库默认级别是RC，比如SQL Server，Oracle），读取的时候不可以修改。\n* 可重复读： 同一个事务里确保每次读取的时候，获得的是同样的数据，但不保障原始数据被其他事务更新（幻读），Mysql InnoDB 就是这个级别。\n* 序列化：所有事物串行处理（牺牲了效率）\n\n* [《理解事务的4种隔离级别》](https://blog.csdn.net/qq_33290787/article/details/51924963)\n* [数据库事务的四大特性及事务隔离级别](https://www.cnblogs.com/z-sm/p/7245981.html)\n\n* [《MySQL的InnoDB的幻读问题 》](http://blog.sina.com.cn/s/blog_499740cb0100ugs7.html)\n	* 幻读的例子非常清楚。\n	* 通过 SELECT ... FOR UPDATE 解决。\n	\n* [《一篇文章带你读懂MySQL和InnoDB》](https://draveness.me/mysql-innodb)\n	* 图解脏读、不可重复读、幻读问题。\n\n\n### MVCC\n\n\n* [《【mysql】关于innodb中MVCC的一些理解》](https://www.cnblogs.com/chenpingzhao/p/5065316.html)\n	* innodb 中 MVCC 用在 Repeatable-Read 隔离级别。\n	* MVCC 会产生幻读问题（更新时异常。）\n\n* [《轻松理解MYSQL MVCC 实现机制》](https://blog.csdn.net/whoamiyang/article/details/51901888)\n\n	* 通过隐藏版本列来实现 MVCC 控制，一列记录创建时间、一列记录删除时间，这里的时间\n	* 每次只操作比当前版本小（或等于）的 行。\n	\n\n\n## 锁\n\n### Java中的锁和同步类\n\n* [《Java中的锁分类》](https://www.cnblogs.com/qifengshi/p/6831055.html)\n	* 主要包括 synchronized、ReentrantLock、和 ReadWriteLock。 \n\n* [《Java并发之AQS详解》](https://www.cnblogs.com/waterystone/p/4920797.html)\n\n* [《Java中信号量 Semaphore》](http://cuisuqiang.iteye.com/blog/2020146)\n	* 有数量控制\n	* 申请用 acquire，申请不要则阻塞；释放用 release。\n\n* [《java开发中的Mutex vs Semaphore》](https://www.cnblogs.com/davidwang456/p/6094947.html)\n	* 简单的说 就是Mutex是排它的，只有一个可以获取到资源， Semaphore也具有排它性，但可以定义多个可以获取的资源的对象。	 \n\n### 公平锁 & 非公平锁\n\n公平锁的作用就是严格按照线程启动的顺序来执行的，不允许其他线程插队执行的；而非公平锁是允许插队的。\n\n* [《公平锁与非公平锁》](https://blog.csdn.net/EthanWhite/article/details/55508357)\n	* 默认情况下 ReentrantLock 和 synchronized 都是非公平锁。ReentrantLock 可以设置成公平锁。\n\n### 悲观锁 \n\n悲观锁如果使用不当（锁的条数过多），会引起服务大面积等待。推荐优先使用乐观锁+重试。\n\n* [《【MySQL】悲观锁&乐观锁》](https://www.cnblogs.com/zhiqian-ali/p/6200874.html)\n	* 乐观锁的方式：版本号+重试方式\n	* 悲观锁：通过 select ... for update 进行行锁(不可读、不可写，share 锁可读不可写)。\n\n* [《Mysql查询语句使用select.. for update导致的数据库死锁分析》](https://www.cnblogs.com/Lawson/p/5008741.html)\n	* mysql的innodb存储引擎实务锁虽然是锁行，但它内部是锁索引的。\n	* 锁相同数据的不同索引条件可能会引起死锁。\n	\n* [《Mysql并发时经典常见的死锁原因及解决方法》](https://www.cnblogs.com/zejin2008/p/5262751.html)\n\n### 乐观锁 & CAS\n\n* [《乐观锁的一种实现方式——CAS》](http://www.importnew.com/20472.html)\n	* 和MySQL乐观锁方式相似，只不过是通过和原值进行比较。	 \n\n### ABA 问题\n\n由于高并发，在CAS下，更新后可能此A非彼A。通过版本号可以解决，类似于上文Mysql 中提到的的乐观锁。\n\n* [《Java CAS 和ABA问题》](https://www.cnblogs.com/549294286/p/3766717.html)\n* [《Java 中 ABA问题及避免》](https://blog.csdn.net/li954644351/article/details/50511879)\n	* AtomicStampedReference 和 AtomicStampedReference。 \n\n### CopyOnWrite容器\n\n可以对CopyOnWrite容器进行并发的读，而不需要加锁。CopyOnWrite并发容器用于读多写少的并发场景。比如白名单，黑名单，商品类目的访问和更新场景，不适合需要数据强一致性的场景。\n\n* [《JAVA中写时复制(Copy-On-Write)Map实现》](https://www.cnblogs.com/hapjin/p/4840107.html)\n	* 实现读写分离，读取发生在原始数据上，写入发生在副本上。  \n	* 不用加锁，通过最终一致实现一致性。\n	\n* [《聊聊并发-Java中的Copy-On-Write容器》](https://blog.csdn.net/a494303877/article/details/53404623)\n\n### RingBuffer \n* [《线程安全的无锁RingBuffer的实现【一个读线程，一个写线程】》](http://www.cnblogs.com/l00l/p/4115001.html)\n\n### 可重入锁 & 不可重入锁\n\n* [《可重入锁和不可重入锁》](https://www.cnblogs.com/dj3839/p/6580765.html)\n	* 通过简单代码举例说明可重入锁和不可重入锁。\n	* 可重入锁指同一个线程可以再次获得之前已经获得的锁。\n	* 可重入锁可以用户避免死锁。\n	* Java中的可重入锁：synchronized 和 java.util.concurrent.locks.ReentrantLock\n\n* [《ReenTrantLock可重入锁（和synchronized的区别）总结》](https://www.cnblogs.com/baizhanshi/p/7211802.html)\n	* synchronized 使用方便，编译器来加锁，是非公平锁。\n	* ReenTrantLock 使用灵活，锁的公平性可以定制。\n	* 相同加锁场景下，推荐使用 synchronized。\n\n### 互斥锁 & 共享锁\n\n互斥锁：同时只能有一个线程获得锁。比如，ReentrantLock 是互斥锁，ReadWriteLock 中的写锁是互斥锁。\n共享锁：可以有多个线程同时或的锁。比如，Semaphore、CountDownLatch 是共享锁，ReadWriteLock 中的读锁是共享锁。\n\n* [《ReadWriteLock场景应用》](https://www.cnblogs.com/liang1101/p/6475555.html)\n\n### 死锁\n* [《“死锁”四个必要条件的合理解释》](https://blog.csdn.net/yunfenglw/article/details/45950305)\n	* 互斥、持有、不可剥夺、环形等待。\n* [Java如何查看死锁？](https://blog.csdn.net/u014039577/article/details/52351626)\n	* JConsole 可以识别死锁。\n	\n* [java多线程系列：死锁及检测](https://blog.csdn.net/bohu83/article/details/51135061)\n	* jstack 可以显示死锁。\n	\n# 操作系统\n\n## 计算机原理\n\n* [《操作系统基础知识——操作系统的原理，类型和结构》](https://segmentfault.com/a/1190000003692840)\n\n## CPU\n\n### 多级缓存\n典型的 CPU 有三级缓存，距离核心越近，速度越快，空间越小。L1 一般 32k，L2 一般 256k，L3 一般12M。内存速度需要200个 CPU 周期，CPU 缓存需要1个CPU周期。\n\n* [《从Java视角理解CPU缓存和伪共享》](https://blog.csdn.net/zero__007/article/details/54089730)\n\n## 进程\n\nTODO\n\n## 线程\n\n* [《线程的生命周期及状态转换详解》](https://blog.csdn.net/asdf_1024/article/details/78978437)\n\n## 协程\n\n* [《终结python协程----从yield到actor模型的实现》](https://www.thinksaas.cn/group/topic/839375/)\n	* 线程的调度是由操作系统负责，协程调度是程序自行负责\n	* 与线程相比，协程减少了无谓的操作系统切换.\n	* 实际上当遇到IO操作时做切换才更有意义，（因为IO操作不用占用CPU），如果没遇到IO操作，按照时间片切换.\n	\n## Linux\n\n* [《Linux 命令大全》](http://www.runoob.com/linux/linux-command-manual.html)\n\n# 设计模式\n\n## 设计模式的六大原则\n* [《设计模式的六大原则》](https://blog.csdn.net/q291611265/article/details/48465113)\n	* 开闭原则：对扩展开放,对修改关闭，多使用抽象类和接口。\n	* 里氏替换原则：基类可以被子类替换，使用抽象类继承,不使用具体类继承。\n	* 依赖倒转原则：要依赖于抽象,不要依赖于具体，针对接口编程,不针对实现编程。\n	* 接口隔离原则：使用多个隔离的接口,比使用单个接口好，建立最小的接口。\n	* 迪米特法则：一个软件实体应当尽可能少地与其他实体发生相互作用，通过中间类建立联系。\n	* 合成复用原则：尽量使用合成/聚合,而不是使用继承。\n\n## 23种常见设计模式\n* [《设计模式》](http://www.runoob.com/design-pattern/design-pattern-tutorial.html)\n* [《23种设计模式全解析》](https://www.cnblogs.com/susanws/p/5510229.html)\n* [《设计模式类图与示例》](https://github.com/ToryZhou/design-pattern)\n\n## 应用场景\n* [《细数JDK里的设计模式》](http://blog.jobbole.com/62314/)\n	* 结构型模式：\n		* 适配器：用来把一个接口转化成另一个接口，如 java.util.Arrays#asList()。\n		* 桥接模式：这个模式将抽象和抽象操作的实现进行了解耦，这样使得抽象和实现可以独立地变化，如JDBC；\n		* 组合模式：使得客户端看来单个对象和对象的组合是同等的。换句话说，某个类型的方法同时也接受自身类型作为参数，如 Map.putAll，List.addAll、Set.addAll。\n		* 装饰者模式：动态的给一个对象附加额外的功能，这也是子类的一种替代方式，如 java.util.Collections#checkedList|Map|Set|SortedSet|SortedMap。\n		* 享元模式：使用缓存来加速大量小对象的访问时间，如 valueOf(int)。\n		* 代理模式：代理模式是用一个简单的对象来代替一个复杂的或者创建耗时的对象，如 java.lang.reflect.Proxy\n		\n	* 创建模式:\n		* 抽象工厂模式：抽象工厂模式提供了一个协议来生成一系列的相关或者独立的对象，而不用指定具体对象的类型，如 java.util.Calendar#getInstance()。\n		* 建造模式(Builder)：定义了一个新的类来构建另一个类的实例，以简化复杂对象的创建，如：java.lang.StringBuilder#append()。\n		* 工厂方法：就是 **一个返*** 回具体对象的方法，而不是多个，如 java.lang.Object#toString()、java.lang.Class#newInstance()。\n		* 原型模式：使得类的实例能够生成自身的拷贝、如：java.lang.Object#clone()。\n		* 单例模式：全局只有一个实例，如 java.lang.Runtime#getRuntime()。\n	* 行为模式：\n		* 责任链模式：通过把请求从一个对象传递到链条中下一个对象的方式，直到请求被处理完毕，以实现对象间的解耦。如 javax.servlet.Filter#doFilter()。\n		* 命令模式：将操作封装到对象内，以便存储，传递和返回，如：java.lang.Runnable。\n		* 解释器模式：定义了一个语言的语法，然后解析相应语法的语句，如，java.text.Format，java.text.Normalizer。\n		* 迭代器模式：提供一个一致的方法来顺序访问集合中的对象，如 java.util.Iterator。\n		* 中介者模式：通过使用一个中间对象来进行消息分发以及减少类之间的直接依赖，java.lang.reflect.Method#invoke()。\n		* 空对象模式：如 java.util.Collections#emptyList()。\n		* 观察者模式：它使得一个对象可以灵活的将消息发送给感兴趣的对象，如 java.util.EventListener。\n		* 模板方法模式：让子类可以重写方法的一部分，而不是整个重写，如 java.util.Collections#sort()。\n\n* [《Spring-涉及到的设计模式汇总》](https://www.cnblogs.com/hwaggLee/p/4510687.html)\n* [《Mybatis使用的设计模式》](https://blog.csdn.net/u012387062/article/details/54719114)\n\n## 单例模式\n* [《单例模式的三种实现 以及各自的优缺点》](https://blog.csdn.net/YECrazy/article/details/79481964)\n* [《单例模式－－反射－－防止序列化破坏单例模式》](https://www.cnblogs.com/ttylinux/p/6498822.html)\n	* 使用枚举类型。\n\n## 责任链模式\nTODO\n\n## MVC\n* [《MVC 模式》](http://www.runoob.com/design-pattern/mvc-pattern.html)\n	* 模型(model)－视图(view)－控制器(controller) \n\n## IOC\n* [《理解 IOC》](https://www.zhihu.com/question/23277575)\n* [《IOC 的理解与解释》](https://www.cnblogs.com/NancyStartOnce/p/6813162.html)\n	* 正向控制：传统通过new的方式。反向控制，通过容器注入对象。\n	* 作用：用于模块解耦。\n	* DI：Dependency Injection，即依赖注入，只关心资源使用，不关心资源来源。\n\n## AOP\n\n* [《轻松理解AOP(面向切面编程)》](https://blog.csdn.net/yanquan345/article/details/19760027)\n* [《Spring AOP详解》](https://www.cnblogs.com/hongwz/p/5764917.html)\n* [《Spring AOP的实现原理》](http://www.importnew.com/24305.html)\n	* Spring AOP使用的动态代理，主要有两种方式：JDK动态代理和CGLIB动态代理。\n* [《Spring AOP 实现原理与 CGLIB 应用》](https://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/)\n	* Spring AOP 框架对 AOP 代理类的处理原则是：如果目标对象的实现类实现了接口，Spring AOP 将会采用 JDK 动态代理来生成 AOP 代理类；如果目标对象的实现类没有实现接口，Spring AOP 将会采用 CGLIB 来生成 AOP 代理类 \n\n\n## UML\n\n* [《UML教程》](https://www.w3cschool.cn/uml_tutorial/)\n\n## 微服务思想\n* [《微服务架构设计》](https://www.cnblogs.com/wintersun/p/6219259.html)\n* [《微服务架构技术栈选型手册》](http://www.infoq.com/cn/articles/micro-service-technology-stack)\n\n### 康威定律\n* [《微服务架构的理论基础 - 康威定律》](https://yq.aliyun.com/articles/8611)\n	* 定律一：组织沟通方式会通过系统设计表达出来，就是说架构的布局和组织结构会有相似。\n	* 定律二：时间再多一件事情也不可能做的完美，但总有时间做完一件事情。一口气吃不成胖子，先搞定能搞定的。\n	* 定律三：线型系统和线型组织架构间有潜在的异质同态特性。种瓜得瓜，做独立自治的子系统减少沟通成本。\n	* 定律四：大的系统组织总是比小系统更倾向于分解。合久必分，分而治之。\n\n* [《微服务架构核⼼20讲》](https://static.geekbang.org/PDF-%E4%BF%AE%E6%94%B9%E7%89%88-%E6%9E%81%E5%AE%A2%E6%97%B6%E9%97%B4-%E5%9B%BE%E7%89%87-%E6%9D%A8%E6%B3%A2-%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84.pdf)\n\n# 运维 & 统计 & 技术支持 \n\n## 常规监控\n\n* [《腾讯业务系统监控的修炼之路》](https://blog.csdn.net/enweitech/article/details/77849205)\n	* 监控的方式：主动、被动、旁路(比如舆情监控)\n	* 监控类型： 基础监控、服务端监控、客户端监控、\n	监控、用户端监控\n	* 监控的目标：全、块、准\n	* 核心指标：请求量、成功率、耗时\n\n* [《开源还是商用？十大云运维监控工具横评》](https://www.oschina.net/news/67525/monitoring-tools)\n	* Zabbix、Nagios、Ganglia、Zenoss、Open-falcon、监控宝、 360网站服务监控、阿里云监控、百度云观测、小蜜蜂网站监测等。\n\n* [《监控报警系统搭建及二次开发经验》](http://developer.51cto.com/art/201612/525373.htm)\n\n**命令行监控工具**\n\n* [《常用命令行监控工具》](https://coderxing.gitbooks.io/architecture-evolution/di-er-pian-ff1a-feng-kuang-yuan-shi-ren/44-an-quan-yu-yun-wei/445-fu-wu-qi-zhuang-tai-jian-ce/4451-ming-ling-xing-gong-ju.html)\n	* top、sar、tsar、nload\n\n* [《20个命令行工具监控 Linux 系统性能》](http://blog.jobbole.com/96846/)\n\n* [《JVM性能调优监控工具jps、jstack、jmap、jhat、jstat、hprof使用详解》](https://my.oschina.net/feichexia/blog/196575)\n\n## APM\n\nAPM —  Application Performance Management\n\n* [《Dapper，大规模分布式系统的跟踪系统》](http://bigbully.github.io/Dapper-translation/)\n\n* [CNCF OpenTracing](http://opentracing.io)，[中文版](https://github.com/opentracing-contrib/opentracing-specification-zh)\n\n* 主要开源软件，按字母排序\n  * [Apache SkyWalking](https://github.com/apache/incubator-skywalking)\n  * [CAT](https://github.com/dianping/cat)\n  * [CNCF jaeger](https://github.com/jaegertracing/jaeger)\n  * [Pinpoint](https://github.com/naver/pinpoint)\n  * [Zipkin](https://github.com/openzipkin/zipkin)\n\n* [《开源APM技术选型与实战》](http://www.infoq.com/cn/articles/apm-Pinpoint-practice)\n	* 主要基于 Google的Dapper（大规模分布式系统的跟踪系统） 思想。\n	\n\n\n## 统计分析\n\n* [《流量统计的基础：埋点》](https://zhuanlan.zhihu.com/p/25195217)\n	* 常用指标：访问与访客、停留时长、跳出率、退出率、转化率、参与度\n\n* [《APP埋点常用的统计工具、埋点目标和埋点内容》](http://www.25xt.com/company/17066.html)\n	* 第三方统计：友盟、百度移动、魔方、App Annie、talking data、神策数据等。\n\n* [《美团点评前端无痕埋点实践》](https://tech.meituan.com/mt_mobile_analytics_practice.html)\n	* 所谓无痕、即通过可视化工具配置采集节点，在前端自动解析配置并上报埋点数据，而非硬编码。 \n\n\n## 持续集成(CI/CD)\n\n* [《持续集成是什么？》](http://www.ruanyifeng.com/blog/2015/09/continuous-integration.html)\n* [《8个流行的持续集成工具》](https://www.testwo.com/article/1170)\n\n### Jenkins\n\n* [《使用Jenkins进行持续集成》](https://www.liaoxuefeng.com/article/001463233913442cdb2d1bd1b1b42e3b0b29eb1ba736c5e000)\n\n### 环境分离\n\n开发、测试、生成环境分离。\n\n* [《开发环境、生产环境、测试环境的基本理解和区》](https://my.oschina.net/sancuo/blog/214904)\n\n## 自动化运维\n\n### Ansible\n* [《Ansible中文权威指南》](http://www.ansible.com.cn/)\n* [《Ansible基础配置和企业级项目实用案例》](https://www.cnblogs.com/heiye123/articles/7855890.html)\n\n### puppet\n* [《自动化运维工具——puppet详解》](https://www.cnblogs.com/keerya/p/8040071.html)\n\n### chef\n* [《Chef 的安装与使用》](https://www.ibm.com/developerworks/cn/cloud/library/1407_caomd_chef/)\n\n## 测试\n\n### TDD 理论\n\n* [《深度解读 - TDD（测试驱动开发）》](https://www.jianshu.com/p/62f16cd4fef3)\n	* 基于测试用例编码功能代码，XP（Extreme Programming）的核心实践.\n	* 好处：一次关注一个点，降低思维负担；迎接需求变化或改善代码的设计；提前澄清需求；快速反馈； \n\n### 单元测试\n\n* [《Java单元测试之JUnit篇》](https://www.cnblogs.com/happyzm/p/6482886.html)\n* [《JUnit 4 与 TestNG 对比》](https://blog.csdn.net/hotdust/article/details/53406086)\n	* TestNG 覆盖 JUnit 功能，适用于更复杂的场景。 \n* [《单元测试主要的测试功能点》](https://blog.csdn.net/wqetfg/article/details/50900512)\n	* 模块接口测试、局部数据结构测试、路径测试 、错误处理测试、边界条件测试 。 \n\n### 压力测试\n\n* [《Apache ab 测试使用指南》](https://blog.csdn.net/blueheart20/article/details/52170790)\n* [《大型网站压力测试及优化方案》](https://www.cnblogs.com/binyue/p/6141088.html)\n* [《10大主流压力/负载/性能测试工具推荐》](http://news.chinabyte.com/466/14126966.shtml)\n* [《真实流量压测工具 tcpcopy应用浅析》](http://quentinxxz.iteye.com/blog/2249799)\n* [《nGrinder 简易使用教程》](https://www.cnblogs.com/jwentest/p/7136727.html)\n\n\n### 全链路压测\n* [《京东618：升级全链路压测方案，打造军演机器人ForceBot》](http://www.infoq.com/cn/articles/jd-618-upgrade-full-link-voltage-test-program-forcebot)\n* [《饿了么全链路压测的探索与实践》](https://zhuanlan.zhihu.com/p/30306892)\n* [《四大语言，八大框架｜滴滴全链路压测解决之道》](https://zhuanlan.zhihu.com/p/28355759)\n* [《全链路压测经验》](https://www.jianshu.com/p/27060fd61f72)\n\n\n### A/B 、灰度、蓝绿测试\n\n* [《技术干货 | AB 测试和灰度发布探索及实践》](https://testerhome.com/topics/11165)\n* [《nginx 根据IP 进行灰度发布》](http://blog.51cto.com/purplegrape/1403123)\n\n* [《蓝绿部署、A/B 测试以及灰度发布》](https://www.v2ex.com/t/344341)\n\n## 虚拟化\n\n* [《VPS的三种虚拟技术OpenVZ、Xen、KVM优缺点比较》](https://blog.csdn.net/enweitech/article/details/52910082)\n\n### KVM\n* [《KVM详解，太详细太深入了，经典》](http://blog.chinaunix.net/uid-20201831-id-5775661.html)\n* [《【图文】KVM 虚拟机安装详解》](https://www.coderxing.com/kvm-install.html)\n\n### Xen\n* [《Xen虚拟化基本原理详解》](https://www.cnblogs.com/sddai/p/5931201.html)\n\n### OpenVZ\n* [《开源Linux容器 OpenVZ 快速上手指南》](https://blog.csdn.net/longerzone/article/details/44829255)\n\n## 容器技术\n\n### Docker\n* [《几张图帮你理解 docker 基本原理及快速入门》](https://www.cnblogs.com/SzeCheng/p/6822905.html)\n* [《Docker 核心技术与实现原理》](https://draveness.me/docker)\n* [《Docker 教程》](http://www.runoob.com/docker/docker-tutorial.html)\n\n## 云技术\n\n### OpenStack\n* [《OpenStack构架知识梳理》](https://www.cnblogs.com/klb561/p/8660264.html)\n\n## DevOps\n* [《一分钟告诉你究竟DevOps是什么鬼？》](https://www.cnblogs.com/jetzhang/p/6068773.html)\n* [《DevOps详解》](http://www.infoq.com/cn/articles/detail-analysis-of-devops)\n\n## 文档管理\n\n* [Confluence-收费文档管理系统](http://www.confluence.cn/)\n* GitLab?\n* Wiki\n\n# 中间件\n\n## Web Server\n\n### Nginx\n* [《Ngnix的基本学习-多进程和Apache的比较》](https://blog.csdn.net/qq_25797077/article/details/52200722)\n	* Nginx 通过异步非阻塞的事件处理机制实现高并发。Apache 每个请求独占一个线程，非常消耗系统资源。\n	* 事件驱动适合于IO密集型服务(Nginx)，多进程或线程适合于CPU密集型服务(Apache)，所以Nginx适合做反向代理，而非web服务器使用。  \n\n* [《nginx与Apache的对比以及优缺点》](https://www.cnblogs.com/cunkouzh/p/5410154.html)\n	* nginx只适合静态和反向代理，不适合处理动态请求。\n\n### OpenResty\n* [官方网站](http://openresty.org/cn/)\n* [《浅谈 OpenResty》](http://www.linkedkeeper.com/detail/blog.action?bid=1034)\n	* 通过 Lua 模块可以在Nginx上进行开发。   \n* [agentzh 的 Nginx 教程](https://openresty.org/download/agentzh-nginx-tutorials-zhcn.html)  \n	\n### Tengine\n* [官方网站](http://tengine.taobao.org/)  \n\n### Apache Httpd\n* [官方网站](http://httpd.apache.org/)\n\n### Tomcat\n\n#### 架构原理\n* [《TOMCAT原理详解及请求过程》](https://www.cnblogs.com/hggen/p/6264475.html)\n* [《Tomcat服务器原理详解》](https://www.cnblogs.com/crazylqy/p/4706223.html)\n* [《Tomcat 系统架构与设计模式,第 1 部分: 工作原理》](https://www.ibm.com/developerworks/cn/java/j-lo-tomcat1/)\n\n* [《四张图带你了解Tomcat系统架构》](https://blog.csdn.net/xlgen157387/article/details/79006434)\n\n* [《JBoss vs. Tomcat: Choosing A Java Application Server》](https://www.futurehosting.com/blog/jboss-vs-tomcat-choosing-a-java-application-server/)\n	* Tomcat 是轻量级的 Serverlet 容器，没有实现全部 JEE 特性（比如持久化和事务处理），但可以通过其他组件代替，比如Spring。\n	* Jboss 实现全部了JEE特性，软件开源免费、文档收费。\n\n#### 调优方案\n\n* [《Tomcat 调优方案》](https://www.cnblogs.com/sunfenqing/p/7339058.html)\n	* 启动NIO模式（或者APR）；调整线程池；禁用AJP连接器（Nginx+tomcat的架构，不需要AJP）； \n\n* [《tomcat http协议与ajp协议》](http://blog.chinaunix.net/uid-20662363-id-3012760.html)\n* [《AJP与HTTP比较和分析》](http://dmouse.iteye.com/blog/1354527)\n	* AJP 协议（8009端口）用于降低和前端Server（如Apache，而且需要支持AJP协议）的连接数(前端)，通过长连接提高性能。\n	* 并发高时，AJP协议优于HTTP协议。\n\n### Jetty\n* [《Jetty 的工作原理以及与 Tomcat 的比较》](https://www.ibm.com/developerworks/cn/java/j-lo-jetty/)\n* [《jetty和tomcat优势比较》](https://blog.csdn.net/doutao6677/article/details/51957288)\n	* 架构比较:Jetty的架构比Tomcat的更为简单。\n	* 性能比较：Jetty和Tomcat性能方面差异不大，Jetty默认采用NIO结束在处理I/O请求上更占优势，Tomcat默认采用BIO处理I/O请求，Tomcat适合处理少数非常繁忙的链接，处理静态资源时性能较差。\n	* 其他方面：Jetty的应用更加快速，修改简单，对新的Servlet规范的支持较好;Tomcat 对JEE和Servlet 支持更加全面。 \n\n\n\n## 缓存\n\n* [《缓存失效策略（FIFO 、LRU、LFU三种算法的区别）》](https://blog.csdn.net/clementad/article/details/48229243)\n\n### 本地缓存\n\n* [《HashMap本地缓存》](https://coderxing.gitbooks.io/architecture-evolution/di-er-pian-ff1a-feng-kuang-yuan-shi-ren/42-xing-neng-zhi-ben-di-huan-cun/421-ying-yong-ceng-ben-di-huan-cun/4211.html)\n\n* [《EhCache本地缓存》](https://coderxing.gitbooks.io/architecture-evolution/di-er-pian-ff1a-feng-kuang-yuan-shi-ren/42-xing-neng-zhi-ben-di-huan-cun/421-ying-yong-ceng-ben-di-huan-cun/4212-ehcache.html)\n	* 堆内、堆外、磁盘三级缓存。\n	* 可按照缓存空间容量进行设置。\n	* 按照时间、次数等过期策略。\n\n* [《Guava Cache》](https://coderxing.gitbooks.io/architecture-evolution/di-er-pian-ff1a-feng-kuang-yuan-shi-ren/42-xing-neng-zhi-ben-di-huan-cun/421-ying-yong-ceng-ben-di-huan-cun/4213-guava-cache.html)\n	* 简单轻量、无堆外、磁盘缓存。\n\n\n* [《Nginx本地缓存》](https://coderxing.gitbooks.io/architecture-evolution/di-er-pian-ff1a-feng-kuang-yuan-shi-ren/42-xing-neng-zhi-ben-di-huan-cun/422-fu-wu-duan-ben-di-huan-cun/nginx-ben-di-huan-cun.html)\n\n* [《Pagespeed—懒人工具，服务器端加速》](https://coderxing.gitbooks.io/architecture-evolution/di-er-pian-ff1a-feng-kuang-yuan-shi-ren/42-xing-neng-zhi-ben-di-huan-cun/422-fu-wu-duan-ben-di-huan-cun/4222-pagespeed.html)\n\n## 客户端缓存\n\n* [《浏览器端缓存》](https://coderxing.gitbooks.io/architecture-evolution/di-er-pian-ff1a-feng-kuang-yuan-shi-ren/42-xing-neng-zhi-ben-di-huan-cun/423-ke-hu-duan-huan-cun.html)\n	* 主要是利用 Cache-Control 参数。\n\n* [《H5 和移动端 WebView 缓存机制解析与实战》](https://mp.weixin.qq.com/s/qHm_dJBhVbv0pJs8Crp77w)\n\n## 服务端缓存\n\n### Web缓存\n\n* [nuster](https://github.com/jiangwenyuan/nuster) - nuster cache\n* [varnish](https://github.com/varnishcache/varnish-cache) - varnish cache\n* [squid](https://github.com/squid-cache/squid) - squid cache\n\n### Memcached\n* [《Memcached 教程》](http://www.runoob.com/Memcached/Memcached-tutorial.html)\n* [《深入理解Memcached原理》](https://blog.csdn.net/chenleixing/article/details/47035453)\n	* 采用多路复用技术提高并发性。\n	* slab分配算法： memcached给Slab分配内存空间，默认是1MB。分配给Slab之后 把slab的切分成大小相同的chunk，Chunk是用于缓存记录的内存空间，Chunk 的大小默认按照1.25倍的速度递增。好处是不会频繁申请内存，提高IO效率，坏处是会有一定的内存浪费。\n* [《Memcached软件工作原理》](https://www.jianshu.com/p/36e5cd400580)\n* [《Memcache技术分享：介绍、使用、存储、算法、优化、命中率》](http://zhihuzeye.com/archives/2361)\n\n* [《memcache 中 add 、 set 、replace 的区别》](https://blog.csdn.net/liu251890347/article/details/37690045)\n	* 区别在于当key存在还是不存在时，返回值是true和false的。\n\n* [**《memcached全面剖析》**](https://pan.baidu.com/s/1qX00Lti?errno=0&errmsg=Auth%20Login%20Sucess&&bduss=&ssnerror=0&traceid=)\n\n### Redis\n\n* [《Redis 教程》](http://www.runoob.com/redis/redis-tutorial.html)\n* [《redis底层原理》](https://blog.csdn.net/wcf373722432/article/details/78678504)\n	* 使用 ziplist 存储链表，ziplist是一种压缩链表，它的好处是更能节省内存空间，因为它所存储的内容都是在连续的内存区域当中的。\n	* 使用 skiplist(跳跃表)来存储有序集合对象、查找上先从高Level查起、时间复杂度和红黑树相当，实现容易，无锁、并发性好。\n* [《Redis持久化方式》](http://doc.redisfans.com/topic/persistence.html)\n	* RDB方式：定期备份快照，常用于灾难恢复。优点：通过fork出的进程进行备份，不影响主进程、RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。缺点：会丢数据。\n	* AOF方式：保存操作日志方式。优点：恢复时数据丢失少，缺点：文件大，回复慢。\n	* 也可以两者结合使用。\n\n* [《分布式缓存--序列3--原子操作与CAS乐观锁》](https://blog.csdn.net/chunlongyu/article/details/53346436)\n\n#### 架构\n* [《Redis单线程架构》](https://blog.csdn.net/sunhuiliang85/article/details/73656830)\n\n#### 回收策略\n* [《redis的回收策略》](https://blog.csdn.net/qq_29108585/article/details/63251491)\n\n### Tair\n\n* [官方网站](https://github.com/alibaba/tair)\n* [《Tair和Redis的对比》](http://blog.csdn.net/farphone/article/details/53522383)\n* 特点：可以配置备份节点数目，通过异步同步到备份节点\n* 一致性Hash算法。\n* 架构：和Hadoop 的设计思想类似，有Configserver，DataServer，Configserver 通过心跳来检测，Configserver也有主备关系。\n\n\n几种存储引擎:\n* MDB，完全内存性，可以用来存储Session等数据。\n* Rdb（类似于Redis），轻量化，去除了aof之类的操作，支持Restfull操作\n* LDB（LevelDB存储引擎），持久化存储，LDB 作为rdb的持久化，google实现，比较高效，理论基础是LSM(Log-Structured-Merge Tree)算法，现在内存中修改数据，达到一定量时（和内存汇总的旧数据一同写入磁盘）再写入磁盘，存储更加高效，县比喻Hash算法。\n* Tair采用共享内存来存储数据，如果服务挂掉（非服务器），重启服务之后，数据亦然还在。\n\n## 消息队列\n\n* [《消息队列-推/拉模式学习 & ActiveMQ及JMS学习》](https://www.cnblogs.com/charlesblc/p/6045238.html)\n	* RabbitMQ 消费者默认是推模式（也支持拉模式）。\n	* Kafka 默认是拉模式。\n	* Push方式：优点是可以尽可能快地将消息发送给消费者，缺点是如果消费者处理能力跟不上，消费者的缓冲区可能会溢出。\n	* Pull方式：优点是消费端可以按处理能力进行拉去，缺点是会增加消息延迟。\n\n* [《Kafka、RabbitMQ、RocketMQ等消息中间件的对比 —— 消息发送性能和区别》](https://blog.csdn.net/yunfeng482/article/details/72856762)\n\n### 消息总线\n\n消息总线相当于在消息队列之上做了一层封装，统一入口，统一管控、简化接入成本。\n\n* [《消息总线VS消息队列》](https://blog.csdn.net/yanghua_kobe/article/details/43877281)\n\n### 消息的顺序\n* [《如何保证消费者接收消息的顺序》](https://www.cnblogs.com/cjsblog/p/8267892.html)\n\n### RabbitMQ\n\n支持事务，推拉模式都是支持、适合需要可靠性消息传输的场景。\n\n* [《RabbitMQ的应用场景以及基本原理介绍》](https://blog.csdn.net/whoamiyang/article/details/54954780)\n* [《消息队列之 RabbitMQ》](https://www.jianshu.com/p/79ca08116d57) \n* [《RabbitMQ之消息确认机制（事务+Confirm）》](https://blog.csdn.net/u013256816/article/details/55515234)\n\n### RocketMQ\nJava实现，推拉模式都是支持，吞吐量逊于Kafka。可以保证消息顺序。\n* [《RocketMQ 实战之快速入门》](https://www.jianshu.com/p/824066d70da8)\n* [《RocketMQ 源码解析》](http://www.iocoder.cn/categories/RocketMQ/?vip&architect-awesome)\n\n### ActiveMQ\n纯Java实现，兼容JMS，可以内嵌于Java应用中。\n* [《ActiveMQ消息队列介绍》](https://www.cnblogs.com/wintersun/p/3962302.html)\n\n### Kafka\n高吞吐量、采用拉模式。适合高IO场景，比如日志同步。\n\n* [官方网站](http://kafka.apache.org/)\n* [《各消息队列对比，Kafka深度解析，众人推荐，精彩好文！》](https://blog.csdn.net/allthesametome/article/details/47362451)\n* [《Kafka分区机制介绍与示例》](http://lxw1234.com/archives/2015/10/538.htm)\n\n### Redis 消息推送\n\n生产者、消费者模式完全是客户端行为，list 和 拉模式实现，阻塞等待采用 blpop 指令。\n\n* [《Redis学习笔记之十：Redis用作消息队列》](https://blog.csdn.net/qq_34212276/article/details/78455004)\n\n### ZeroMQ\n TODO\n\n\n## 定时调度\n\n### 单机定时调度\n\n* [《linux定时任务cron配置》](https://www.cnblogs.com/shuaiqing/p/7742382.html)\n\n* [《Linux cron运行原理》](https://my.oschina.net/daquan/blog/483305)\n	* fork 进程 + sleep 轮询\n\n* [《Quartz使用总结》](https://www.cnblogs.com/drift-ice/p/3817269.html)\n* [《Quartz源码解析 ---- 触发器按时启动原理》](https://blog.csdn.net/wenniuwuren/article/details/42082981/)\n* [《quartz原理揭秘和源码解读》](https://www.jianshu.com/p/bab8e4e32952)\n	* 定时调度在 QuartzSchedulerThread 代码中，while()无限循环，每次循环取出时间将到的trigger，触发对应的job，直到调度器线程被关闭。\n\n\n### 分布式定时调度\n\n* [《这些优秀的国产分布式任务调度系统，你用过几个？》](https://blog.csdn.net/qq_16216221/article/details/70314337)\n	* opencron、LTS、XXL-JOB、Elastic-Job、Uncode-Schedule、Antares\n\n* [《Quartz任务调度的基本实现原理》](https://www.cnblogs.com/zhenyuyaodidiao/p/4755649.html)\n	* Quartz集群中，独立的Quartz节点并不与另一其的节点或是管理节点通信，而是通过相同的数据库表来感知到另一Quartz应用的 \n* [《Elastic-Job-Lite 源码解析》](http://www.iocoder.cn/categories/Elastic-Job-Lite/?vip&architect-awesome)\n* [《Elastic-Job-Cloud 源码解析》](http://www.iocoder.cn/categories/Elastic-Job-Cloud/?vip&architect-awesome)\n\n\n## RPC\n\n* [《从零开始实现RPC框架 - RPC原理及实现》](https://blog.csdn.net/top_code/article/details/54615853)\n	* 核心角色：Server: 暴露服务的服务提供方、Client: 调用远程服务的服务消费方、Registry: 服务注册与发现的注册中心。\n\n* [《分布式RPC框架性能大比拼 dubbo、motan、rpcx、gRPC、thrift的性能比较》](https://blog.csdn.net/testcs_dn/article/details/78050590)\n\n### Dubbo\n* [官方网站](http://dubbo.apache.org/)\n* [dubbo实现原理简单介绍](https://www.cnblogs.com/steven520213/p/7606598.html)\n\n** SPI **\nTODO\n\n### Thrift\n* [官方网站](http://thrift.apache.org/)\n* [《Thrift RPC详解》](https://blog.csdn.net/kesonyk/article/details/50924489)\n	* 支持多语言，通过中间语言定义接口。\n\n### gRPC\n\n服务端可以认证加密，在外网环境下，可以保证数据安全。\n\n* [官方网站](https://grpc.io/)\n* [《你应该知道的RPC原理》](https://www.cnblogs.com/LBSer/p/4853234.html)\n\n\n## 数据库中间件\n\n### Sharding Jdbc\n\n* [官网](http://shardingjdbc.io/)\n* [源码解析](http://www.iocoder.cn/categories/Sharding-JDBC/?vip&architect-awesome)\n\n## 日志系统\n\n### 日志搜集\n\n* [《从零开始搭建一个ELKB日志收集系统》](http://cjting.me/misc/build-log-system-with-elkb/)\n* [《用ELK搭建简单的日志收集分析系统》](https://blog.csdn.net/lzw_2006/article/details/51280058)\n* [《日志收集系统-探究》](https://www.cnblogs.com/beginmind/p/6058194.html)\n\n## 配置中心\n\n* [Apollo - 携程开源的配置中心应用](https://github.com/ctripcorp/apollo)\n	* Spring Boot 和 Spring Cloud\n	* 支持推、拉模式更新配置\n	* 支持多种语言 \n\n* [《基于zookeeper实现统一配置管理》](https://blog.csdn.net/u011320740/article/details/78742625)\n\n* [《 Spring Cloud Config 分布式配置中心使用教程》](https://www.cnblogs.com/shamo89/p/8016908.html)\n\nservlet 3.0 异步特性可用于配置中心的客户端\n* [《servlet3.0 新特性——异步处理》](https://www.cnblogs.com/dogdogwang/p/7151866.html)\n\n## API 网关\n\n主要职责：请求转发、安全认证、协议转换、容灾。\n\n* [《API网关那些儿》](http://yunlzheng.github.io/2017/03/14/the-things-about-api-gateway/)\n* [《谈API网关的背景、架构以及落地方案》](http://www.infoq.com/cn/news/2016/07/API-background-architecture-floo)\n\n* [《使用Zuul构建API Gateway》](https://blog.csdn.net/zhanglh046/article/details/78651993)\n* [《Spring Cloud Gateway 源码解析》](http://www.iocoder.cn/categories/Spring-Cloud-Gateway/?vip&architect-awesome)\n* [《HTTP API网关选择之一Kong介绍》](https://mp.weixin.qq.com/s/LIq2CiXJQmmjBC0yvYLY5A)\n\n# 网络\n\n\n## 协议\n\n### OSI 七层协议\n\n* [《OSI七层协议模型、TCP/IP四层模型学习笔记》](https://www.cnblogs.com/Robin-YB/p/6668762.html)\n\n### TCP/IP\n* [《深入浅出 TCP/IP 协议》](https://www.cnblogs.com/onepixel/p/7092302.html)\n* [《TCP协议中的三次握手和四次挥手》](https://blog.csdn.net/whuslei/article/details/6667471/)\n\n### HTTP\n* [《http协议详解(超详细)》](https://www.cnblogs.com/wangning528/p/6388464.html)\n\n### HTTP2.0\n* [《HTTP 2.0 原理详细分析》](https://blog.csdn.net/zhuyiquan/article/details/69257126)\n* [《HTTP2.0的基本单位为二进制帧》](https://blog.csdn.net/u012657197/article/details/77877840)\n	* 利用二进制帧负责传输。\n	* 多路复用。\n\n### HTTPS\n* [《https原理通俗了解》](https://www.cnblogs.com/zhangshitong/p/6478721.html)\n	* 使用非对称加密协商加密算法\n	* 使用对称加密方式传输数据\n	* 使用第三方机构签发的证书，来加密公钥，用于公钥的安全传输、防止被中间人串改。\n\n* [《八大免费SSL证书-给你的网站免费添加Https安全加密》](https://blog.csdn.net/enweitech/article/details/53213862)\n\n## 网络模型\n\n* [《web优化必须了解的原理之I/o的五种模型和web的三种工作模式》](http://blog.51cto.com/litaotao/1289790)\n	* 五种I/O模型：阻塞I/O，非阻塞I/O，I/O复用、事件(信号)驱动I/O、异步I/O，前四种I/O属于同步操作，I/O的第一阶段不同、第二阶段相同，最后的一种则属于异步操作。\n	* 三种 Web Server 工作方式：Prefork(多进程)、Worker方式(线程方式)、Event方式。\n\n* [《select、poll、epoll之间的区别总结》](http://www.cnblogs.com/Anker/p/3265058.html)\n	* select，poll，epoll本质上都是同步I/O，因为他们都需要在读写事件就绪后自己负责进行读写，也就是说这个读写过程是阻塞的。\n	* select 有打开文件描述符数量限制，默认1024（2048 for x64），100万并发，就要用1000个进程、切换开销大；poll采用链表结构，没有数量限制。\n	* select，poll “醒着”的时候要遍历整个fd集合，而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了，通过回调机制节省大量CPU时间；select，poll每次调用都要把fd集合从用户态往内核态拷贝一次，而epoll只要一次拷贝。\n	* poll会随着并发增加，性能逐渐下降，epoll采用红黑树结构，性能稳定，不会随着连接数增加而降低。\n	\n* [《select，poll，epoll比较  》](http://xingyunbaijunwei.blog.163.com/blog/static/76538067201241685556302/)\n	* 在连接数少并且连接都十分活跃的情况下，select和poll的性能可能比epoll好，毕竟epoll的通知机制需要很多函数回调。\n\n* [《深入理解Java NIO》](https://www.cnblogs.com/geason/p/5774096.html)\n	* NIO 是一种同步非阻塞的 IO 模型。同步是指线程不断轮询 IO 事件是否就绪，非阻塞是指线程在等待 IO 的时候，可以同时做其他任务\n\n* [《BIO与NIO、AIO的区别》](https://blog.csdn.net/skiof007/article/details/52873421)\n\n* [《两种高效的服务器设计模型：Reactor和Proactor模型》](https://blog.csdn.net/u013074465/article/details/46276967)\n\n### Epoll\n\n* [《epoll使用详解（精髓）》](https://www.cnblogs.com/fnlingnzb-learner/p/5835573.html)\n\n### Java NIO\n* [《深入理解Java NIO》](https://www.cnblogs.com/geason/p/5774096.html)\n* [《Java NIO编写Socket服务器的一个例子》](https://blog.csdn.net/xidianliuy/article/details/51612676)\n\n### kqueue\n* [《kqueue用法简介》](http://www.cnblogs.com/luminocean/p/5631336.html)\n\n## 连接和短连接\n\n* [《TCP/IP系列——长连接与短连接的区别》](https://www.cnblogs.com/pangguoping/p/5571422.html)\n\n## 框架\n\n* [《Netty原理剖析》](https://blog.csdn.net/excellentyuxiao/article/details/53390408)\n	* Reactor 模式介绍。\n	* Netty 是 Reactor 模式的一种实现。\n\n## 零拷贝（Zero-copy）\n* [《对于 Netty ByteBuf 的零拷贝(Zero Copy) 的理解》](https://www.cnblogs.com/xys1228/p/6088805.html)\n	* 多个物理分离的buffer，通过逻辑上合并成为一个，从而避免了数据在内存之间的拷贝。\n\n## 序列化(二进制协议)\n\n### Hessian\n* [《Hessian原理分析》](https://www.cnblogs.com/happyday56/p/4268249.html)\nBinary-RPC;不仅仅是序列化\n\n### Protobuf\n* [《Protobuf协议的Java应用例子》](https://blog.csdn.net/antgan/article/details/52103966)\nGoolge出品、占用空间和效率完胜其他序列化类库，如Hessian；需要编写  .proto 文件。\n* [《Protocol Buffers序列化协议及应用》](https://worktile.com/tech/share/prototol-buffers)\n  	* 关于协议的解释；缺点：可读性差;\n\n* [《简单的使用 protobuf 和 protostuff》](https://blog.csdn.net/eric520zenobia/article/details/53766571)\n	* protostuff 的好处是不用写 .proto 文件，Java 对象直接就可以序列化。\n\n# 数据库\n## 基础理论\n### 数据库设计的三大范式\n* [《数据库的三大范式以及五大约束》](https://www.cnblogs.com/waj6511988/p/7027127.html)\n	* 第一范式：数据表中的每一列（每个字段）必须是不可拆分的最小单元，也就是确保每一列的原子性；\n	* 第二范式（2NF）：满足1NF后，要求表中的所有列，都必须依赖于主键，而不能有任何一列与主键没有关系，也就是说一个表只描述一件事情；\n	* 第三范式：必须先满足第二范式（2NF），要求：表中的每一列只与主键直接相关而不是间接相关，（表中的每一列只能依赖于主键）；\n\n## MySQL\n\n### 原理\n* [《MySQL的InnoDB索引原理详解》](http://www.admin10000.com/document/5372.html)\n\n* [《MySQL存储引擎－－MyISAM与InnoDB区别》](https://blog.csdn.net/xifeijian/article/details/20316775)\n	* 两种类型最主要的差别就是Innodb 支持事务处理与外键和行级锁\n\n* [《myisam和innodb索引实现的不同》](https://www.2cto.com/database/201211/172380.html)\n\n### InnoDB\n\n* [《一篇文章带你读懂Mysql和InnoDB》](https://my.oschina.net/kailuncen/blog/1504217)\n\n### 优化\n\n* [《MySQL36条军规》](http://vdisk.weibo.com/s/muWOT)\n\n* [《MYSQL性能优化的最佳20+条经验》](https://www.cnblogs.com/zhouyusheng/p/8038224.html)\n* [《SQL优化之道》](https://blog.csdn.net/when_less_is_more/article/details/70187459)\n* [《mysql数据库死锁的产生原因及解决办法》](https://www.cnblogs.com/sivkun/p/7518540.html)\n* [《导致索引失效的可能情况》](https://blog.csdn.net/monkey_d_feilong/article/details/52291556)\n* [《 MYSQL分页limit速度太慢优化方法》](https://blog.csdn.net/zy_281870667/article/details/51604540)\n	* 原则上就是缩小扫描范围。\n\n\n### 索引\n\n#### 聚集索引, 非聚集索引\n\n* [《MySQL 聚集索引/非聚集索引简述》](https://blog.csdn.net/no_endless/article/details/77073549)\n* [《MyISAM和InnoDB的索引实现》](https://www.cnblogs.com/zlcxbb/p/5757245.html)\n\nMyISAM 是非聚集，InnoDB 是聚集\n\n#### 复合索引\n\n* [《复合索引的优点和注意事项》](https://www.cnblogs.com/summer0space/p/7247778.html)\n	* 文中有一处错误：\n	> 对于复合索引,在查询使用时,最好将条件顺序按找索引的顺序,这样效率最高; select * from table1 where col1=A AND col2=B AND col3=D 如果使用 where col2=B AND col1=A 或者 where col2=B 将不会使用索引\n	* 原文中提到索引是按照“col1，col2，col3”的顺序创建的，而mysql在按照最左前缀的索引匹配原则，且会自动优化 where 条件的顺序，当条件中只有 col2=B AND col1=A 时，会自动转化为 col1=A AND col2=B，所以依然会使用索引。\n	\n* [《MySQL查询where条件的顺序对查询效率的影响》](https://www.cnblogs.com/acode/p/7489258.html)\n	\n#### 自适应哈希索引(AHI)\n\n* [《InnoDB存储引擎——自适应哈希索引》](https://blog.csdn.net/Linux_ever/article/details/62043708)\n\n\n### explain\n* [《MySQL 性能优化神器 Explain 使用分析》](https://segmentfault.com/a/1190000008131735)\n\n## NoSQL\n\n### MongoDB\n\n* [MongoDB 教程](http://www.runoob.com/mongodb/mongodb-tutorial.html)\n* [《Mongodb相对于关系型数据库的优缺点》](http://mxdxm.iteye.com/blog/2093603)\n	* 优点：弱一致性（最终一致），更能保证用户的访问速度；内置GridFS，支持大容量的存储；Schema-less 数据库，不用预先定义结构；内置Sharding；相比于其他NoSQL，第三方支持丰富；性能优越；\n	* 缺点：mongodb不支持事务操作；mongodb占用空间过大；MongoDB没有如MySQL那样成熟的维护工具，这对于开发和IT运营都是个值得注意的地方；\n\n### Hbase\n\n* [《简明 HBase 入门教程（开篇）》](http://www.thebigdata.cn/HBase/35831.html)\n* [《深入学习HBase架构原理》](https://www.cnblogs.com/qiaoyihang/p/6246424.html)\n* [《传统的行存储和（HBase）列存储的区别》](https://blog.csdn.net/youzhouliu/article/details/67632882)\n\n\n* [《Hbase与传统数据库的区别》](https://blog.csdn.net/lifuxiangcaohui/article/details/39891099)\n	* 空数据不存储，节省空间，且适用于并发。\n\n* [《HBase Rowkey设计》](https://blog.csdn.net/u014091123/article/details/73163088)\n	* rowkey 按照字典顺序排列，便于批量扫描。\n	* 通过散列可以避免热点。\n\n# 搜索引擎\n\n## 搜索引擎原理\n\n* [《倒排索引--搜索引擎入门》](https://www.jianshu.com/p/0193dc44135b)\n\n## Lucene\n* [《Lucene入门简介》](https://www.cnblogs.com/rodge-run/p/6551152.html)\n\n## Elasticsearch\n\n* [《Elasticsearch学习，请先看这一篇！》](https://blog.csdn.net/laoyang360/article/details/52244917)\n* [《Elasticsearch索引原理》](https://blog.csdn.net/cyony/article/details/65437708)\n\n## Solr\n* [《 Apache Solr入门教程》](https://blog.csdn.net/u011936655/article/details/51960005)\n* [《elasticsearch与solr比较》](https://blog.csdn.net/convict_eva/article/details/53537837)\n\n## sphinx \n* [《Sphinx 的介绍和原理探索》](http://blog.jobbole.com/101672/)\n\n# 性能\n\n## 性能优化方法论\n\n* [《15天的性能优化工作，5方面的调优经验》](https://blog.csdn.net/huangwenyi1010/article/details/72673447?ref=myread)\n	* 代码层面、业务层面、数据库层面、服务器层面、前端优化。\n\n* [《系统性能优化的几个方面》](https://blog.csdn.net/tenglizhe/article/details/44563135)\n\n## 容量评估\n* [《联网性能与容量评估的方法论和典型案例》](https://blog.csdn.net/u012528360/article/details/70054156)\n* [《互联网架构，如何进行容量设计？》](https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651959542&idx=1&sn=2494bbea9a855e0e1c3ccd6d2562a600&scene=21#wechat_redirect)\n	* 评估总访问量、评估平均访问量QPS、评估高峰QPS、评估系统、单机极限QPS\n\n## CDN 网络\n\n* [《CDN加速原理》](https://www.cnblogs.com/wxiaona/p/5867685.html)\n* [《国内有哪些比较好的 CDN？》](https://www.zhihu.com/question/20536932)\n\n## 连接池\n\n* [《主流Java数据库连接池比较与开发配置实战》](https://blog.csdn.net/fysuccess/article/details/66972554)\n\n## 性能调优\n\n* [《九大Java性能调试工具，必备至少一款》](https://blog.csdn.net/yethyeth/article/details/73266455)\n\n\n# 大数据\n\n## 流式计算\n\n### Storm\n* [官方网站](http://storm.apache.org/)\n* [《最详细的Storm入门教程》](https://blog.csdn.net/uisoul/article/details/77989927)\n\n### Flink\n* [《Flink之一 Flink基本原理介绍》](https://blog.csdn.net/lisi1129/article/details/54844919)\n\n### Kafka Stream\n* [《Kafka Stream调研：一种轻量级流计算模式》](https://yq.aliyun.com/articles/58382)\n\n### 应用场景\n\n例如：\n\n* 广告相关实时统计；\n* 推荐系统用户画像标签实时更新；\n* 线上服务健康状况实时监测；\n* 实时榜单；\n* 实时数据统计。\n\n## Hadoop\n\n* [《用通俗易懂的话说下hadoop是什么,能做什么》](https://blog.csdn.net/houbin0912/article/details/72967178)\n* [《史上最详细的Hadoop环境搭建》](http://gitbook.cn/books/5954c9600326c7705af8a92a/index.html)\n\n### HDFS\n* [《【Hadoop学习】HDFS基本原理》](https://segmentfault.com/a/1190000011575458)\n\n### MapReduce\n* [《用通俗易懂的大白话讲解Map/Reduce原理》](https://blog.csdn.net/oppo62258801/article/details/72884633)\n* [《 简单的map-reduce的java例子》](https://blog.csdn.net/foye12/article/details/78358292)\n\n### Yarn\n* [《初步掌握Yarn的架构及原理》](http://www.cnblogs.com/codeOfLife/p/5492740.html)\n\n## Spark\n* [《Spark(一): 基本架构及原理》](http://www.cnblogs.com/tgzhu/p/5818374.html)\n\n\n# 安全\n\n## web 安全\n\n### XSS\n* [《xss攻击原理与解决方法》](https://blog.csdn.net/qq_21956483/article/details/54377947)\n### CSRF\n* [《CSRF原理及防范》](https://coderxing.gitbooks.io/architecture-evolution/di-san-pian-ff1a-bu-luo/641-web-an-quan-fang-fan/6412-csrf.html)\n\n### SQL 注入\n\n* [《SQL注入》](https://coderxing.gitbooks.io/architecture-evolution/di-san-pian-ff1a-bu-luo/641-web-an-quan-fang-fan/6413-sql-zhu-ru.html)\n\n### Hash Dos\n\n\n* [《邪恶的JAVA HASH DOS攻击》](http://www.freebuf.com/articles/web/14199.html)\n	* 利用JsonObject 上传大Json，JsonObject 底层使用HashMap；不同的数据产生相同的hash值，使得构建Hash速度变慢，耗尽CPU。\n* [《一种高级的DoS攻击-Hash碰撞攻击》](http://blog.it2048.cn/article_hash-collision.html )\n* [《关于Hash Collision DoS漏洞：解析与解决方案》](http://www.iteye.com/news/23939/)\n\n### 脚本注入\n\n* [《上传文件漏洞原理及防范》](https://coderxing.gitbooks.io/architecture-evolution/di-san-pian-ff1a-bu-luo/641-web-an-quan-fang-fan/6414-shang-chuan-wen-jian-guo-lv.html)\n\n### 漏洞扫描工具\n* [《DVWA》](https://coderxing.gitbooks.io/architecture-evolution/di-san-pian-ff1a-bu-luo/6421-dvwa.html)\n* [W3af](https://coderxing.gitbooks.io/architecture-evolution/di-san-pian-ff1a-bu-luo/w3af.html)\n* [OpenVAS详解](https://blog.csdn.net/xygg0801/article/details/53610640)\n\n### 验证码\n\n* [《验证码原理分析及实现》](https://blog.csdn.net/niaonao/article/details/51112686)\n\n* [《详解滑动验证码的实现原理》](https://my.oschina.net/jiangbianwanghai/blog/1031031)\n	* 滑动验证码是根据人在滑动滑块的响应时间，拖拽速度，时间，位置，轨迹，重试次数等来评估风险。\n\n* [《淘宝滑动验证码研究》](https://www.cnblogs.com/xcj26/p/5242758.html)\n\n## DDoS 防范\n* [《学习手册：DDoS的攻击方式及防御手段》](http://netsecurity.51cto.com/art/201601/503799.htm)\n* [《免费DDoS攻击测试工具大合集》](http://netsecurity.51cto.com/art/201406/442756.htm)\n\n## 用户隐私信息保护\n\n1. 用户密码非明文保存，加动态salt。\n2. 身份证号，手机号如果要显示，用 “\\*” 替代部分字符。\n3. 联系方式在的显示与否由用户自己控制。\n4. TODO\n\n* [《个人隐私包括哪些》](https://zhidao.baidu.com/question/1988017976673661587.html)\n* [《在互联网上，隐私的范围包括哪些？》](https://www.zhihu.com/question/20137108)\n\n* [《用户密码保存》](https://coderxing.gitbooks.io/architecture-evolution/di-san-pian-ff1a-bu-luo/642-shu-ju-jia-mi/6425-jia-mi-chang-jing-ff1a-yong-hu-mi-ma-bao-cun.html)\n\n## 序列化漏洞\n* [《Lib之过？Java反序列化漏洞通用利用分析》](https://blog.chaitin.cn/2015-11-11_java_unserialize_rce/)\n\n## 加密解密\n\n### 对称加密\n\n* [《常见对称加密算法》](https://coderxing.gitbooks.io/architecture-evolution/di-san-pian-ff1a-bu-luo/642-shu-ju-jia-mi/6421-chang-jian-dui-cheng-jia-mi-suan-fa.html)\n	* DES、3DES、Blowfish、AES\n	* DES 采用 56位秘钥，Blowfish 采用1到448位变长秘钥，AES 128，192和256位长度的秘钥。\n	* DES 秘钥太短（只有56位）算法目前已经被 AES 取代，并且 AES 有硬件加速，性能很好。\n	\n### 哈希算法\n* [《常用的哈希算法》](https://coderxing.gitbooks.io/architecture-evolution/di-san-pian-ff1a-bu-luo/642-shu-ju-jia-mi/6422-chang-jian-ha-xi-suan-fa-and-hmac.html)\n	* MD5 和 SHA-1 已经不再安全，已被弃用。\n	* 目前 SHA-256 是比较安全的。\n	\n* [《基于Hash摘要签名的公网URL签名验证设计方案》](https://blog.csdn.net/zhangruhong168/article/details/78033202)\n\n### 非对称加密\n* [《常见非对称加密算法》](https://coderxing.gitbooks.io/architecture-evolution/di-san-pian-ff1a-bu-luo/642-shu-ju-jia-mi/6424-chang-yong-fei-dui-cheng-jia-mi-suan-fa.html)\n	* RSA、DSA、ECDSA(螺旋曲线加密算法)\n	* 和 RSA 不同的是 DSA 仅能用于数字签名，不能进行数据加密解密，其安全性和RSA相当，但其性能要比RSA快。\n	* 256位的ECC秘钥的安全性等同于3072位的RSA秘钥。\n\n		[《区块链的加密技术》](http://baijiahao.baidu.com/s?id=1578348858092033763&wfr=spider&for=pc)	\n\n\n## 服务器安全\n* [《Linux强化论：15步打造一个安全的Linux服务器》](http://www.freebuf.com/articles/system/121540.html)\n\n## 数据安全\n\n### 数据备份\n\nTODO\n\n## 网络隔离\n\n### 内外网分离\n\nTODO\n\n### 登录跳板机\n在内外环境中通过跳板机登录到线上主机。\n* [《搭建简易堡垒机》](http://blog.51cto.com/zero01/2062618)\n\n## 授权、认证\n### RBAC \n* [《基于组织角色的权限设计》](https://www.cnblogs.com/zq8024/p/5003050.html)\n* [《权限系统与RBAC模型概述》](https://www.cnblogs.com/shijiaqi1066/p/3793894.html)\n* [《Spring整合Shiro做权限控制模块详细案例分析》](https://blog.csdn.net/he90227/article/details/38663553)\n\n### OAuth2.0\n* [《理解OAuth 2.0》](http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html)\n* [《一张图搞定OAuth2.0》](https://www.cnblogs.com/flashsun/p/7424071.html)\n\n### 双因素认证（2FA）\n\n2FA - Two-factor authentication，用于加强登录验证\n\n常用做法是 登录密码 + 手机验证码（或者令牌Key，类似于与网银的 USB key）\n\n* 【《双因素认证（2FA）教程》】(http://www.ruanyifeng.com/blog/2017/11/2fa-tutorial.html)\n\n### 单点登录(SSO)\n\n* [《单点登录原理与简单实现》](https://www.cnblogs.com/ywlaker/p/6113927.html)\n\n* [CAS单点登录框架](https://github.com/apereo/cas)\n\n# 常用开源框架\n\n## 开源协议\n\n* [《开源协议的选择》](https://coderxing.gitbooks.io/architecture-evolution/chapter1/di-yi-zhang-ff1a-zhun-bei-qi-cheng/12-guan-yu-kai-yuan/123-kai-yuan-xie-yi-de-xuan-ze.html)\n\n* [如何选择一个开源软件协议](http://choosealicense.online/)\n\n## 日志框架\n\n### Log4j、Log4j2\n* [《log4j 详细讲解》](https://blog.csdn.net/u012422446/article/details/51199724)\n* [《log4j2 实际使用详解》](https://blog.csdn.net/vbirdbest/article/details/71751835)\n* [《Log4j1,Logback以及Log4j2性能测试对比》](https://my.oschina.net/OutOfMemory/blog/789267)\n	* Log4J 异步日志性能优异。 \n\n### Logback\n* [《最全LogBack 详解、含java案例和配置说明》](https://blog.csdn.net/rulon147/article/details/52620541)\n\n## ORM\n\n* [《ORM框架使用优缺点》](https://blog.csdn.net/sinat_34093604/article/details/53082000)\n	* 主要目的是为了提高开发效率。 \n\n**MyBatis：**\n\n* [《mybatis缓存机制详解》](https://www.cnblogs.com/winclpt/articles/7511672.html)\n	* 一级缓存是SqlSession级别的缓存，缓存的数据只在SqlSession内有效\n	* 二级缓存是mapper级别的缓存，同一个namespace公用这一个缓存，所以对SqlSession是共享的；使用 LRU 机制清理缓存，通过 cacheEnabled 参数开启。  \n\n* [《MyBatis学习之代码生成器Generator》](https://blog.csdn.net/baidu_32877851/article/details/53959268)\n\n## 网络框架\n\nTODO\n\n## Web 框架\n\n### Spring 家族\n**Spring**\n* [Spring 简明教程](https://www.w3cschool.cn/wkspring/)\n\n**Spring Boot**\n* [官方网站](http://projects.spring.io/spring-boot/)\n* [《Spring Boot基础教程》](http://blog.didispace.com/Spring-Boot%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/)\n\n**Spring Cloud**\n\n* [Spring Boot 中文索引站](http://springboot.fun/)\n* [Spring Cloud 中文文档](https://springcloud.cc/)\n* [《Spring Cloud基础教程》](http://blog.didispace.com/Spring-Cloud%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/)\n\n## 工具框架\n\n* [《Apache Commons 工具类介绍及简单使用》](https://www.cnblogs.com/crazylqy/p/4872236.html)\n* [《Google guava 中文教程》](http://ifeve.com/google-guava/)\n\n\n# 分布式设计\n\n## 扩展性设计\n\n* [《架构师不可不知的十大可扩展架构》](https://blog.csdn.net/hemin1003/article/details/53633926)\n	* 总结下来，通用的套路就是分布、缓存及异步处理。 \n\n* [《可扩展性设计之数据切分》](https://yq.aliyun.com/articles/38119)\n	* 水平切分+垂直切分\n	* 利用中间件进行分片如，MySQL Proxy。\n	* 利用分片策略进行切分，如按照ID取模。 \n* [《说说如何实现可扩展性的大型网站架构》](https://blog.csdn.net/deniro_li/article/details/78458306)\n	* 分布式服务+消息队列。\n\n* [《大型网站技术架构（七）--网站的可扩展性架构》](https://blog.csdn.net/chaofanwei/article/details/29191073)\n\n## 稳定性 & 高可用\n\n* [《系统设计：关于高可用系统的一些技术方案》](https://blog.csdn.net/hustspy1990/article/details/78008324)\n	* 可扩展：水平扩展、垂直扩展。 通过冗余部署，避免单点故障。\n	* 隔离：避免单一业务占用全部资源。避免业务之间的相互影响 2. 机房隔离避免单点故障。\n	* 解耦：降低维护成本，降低耦合风险。减少依赖，减少相互间的影响。\n	* 限流：滑动窗口计数法、漏桶算法、令牌桶算法等算法。遇到突发流量时，保证系统稳定。\n	* 降级：紧急情况下释放非核心功能的资源。牺牲非核心业务，保证核心业务的高可用。\n	* 熔断：异常情况超出阈值进入熔断状态，快速失败。减少不稳定的外部依赖对核心服务的影响。\n	* 自动化测试：通过完善的测试，减少发布引起的故障。\n	* 灰度发布：灰度发布是速度与安全性作为妥协，能够有效减少发布故障。\n\n\n* [《关于高可用的系统》](https://coolshell.cn/articles/17459.html)\n	* 设计原则：数据不丢(持久化)；服务高可用(服务副本)；绝对的100%高可用很难，目标是做到尽可能多的9，如99.999%（全年累计只有5分钟）。	 \n\n### 硬件负载均衡\n\n* [《转！！负载均衡器技术Nginx和F5的优缺点对比》](https://www.cnblogs.com/wuyun-blog/p/6186198.html)\n	* 主要是和F5对比。\n\n* [《软/硬件负载均衡产品 你知多少？》](https://www.cnblogs.com/lcword/p/5773296.html)\n\n### 软件负载均衡\n\n* [《几种负载均衡算法》](https://www.cnblogs.com/tianzhiliang/articles/2317808.html)\n	轮寻、权重、负载、最少连接、QoS\n* [《DNS负载均衡》](https://coderxing.gitbooks.io/architecture-evolution/di-san-pian-ff1a-bu-luo/611-dns-fang-shi.html)\n	* 配置简单，更新速度慢。 \n* [《Nginx负载均衡》](https://coderxing.gitbooks.io/architecture-evolution/di-san-pian-ff1a-bu-luo/613-nginx-fu-zai-jun-heng.html)\n	* 简单轻量、学习成本低；主要适用于web应用。\n\n*  [《借助LVS+Keepalived实现负载均衡 》](https://www.cnblogs.com/edisonchou/p/4281978.html)\n	* 配置比较负载、只支持到4层，性能较高。\n\n* [《HAProxy用法详解 全网最详细中文文档》](http://www.ttlsa.com/linux/haproxy-study-tutorial/)\n	* 支持到七层（比如HTTP）、功能比较全面，性能也不错。\n\n* [《Haproxy+Keepalived+MySQL实现读均衡负载》](http://blog.itpub.net/25704976/viewspace-1319781/)\n	* 主要是用户读请求的负载均衡。\n\n* [《rabbitmq+haproxy+keepalived实现高可用集群搭建》](https://www.cnblogs.com/lylife/p/5584019.html)\n\n### 限流\n\n* [《谈谈高并发系统的限流》](https://www.cnblogs.com/haoxinyue/p/6792309.html)\n	* 计数器：通过滑动窗口计数器，控制单位时间内的请求次数，简单粗暴。\n	* 漏桶算法：固定容量的漏桶，漏桶满了就丢弃请求，比较常用。\n	* 令牌桶算法：固定容量的令牌桶，按照一定速率添加令牌，处理请求前需要拿到令牌，拿不到令牌则丢弃请求，或进入丢队列，可以通过控制添加令牌的速率，来控制整体速度。Guava 中的 RateLimiter 是令牌桶的实现。\n	* Nginx 限流：通过 `limit_req` 等模块限制并发连接数。\n\n### 应用层容灾\n\n* [《防雪崩利器：熔断器 Hystrix 的原理与使用》](https://segmentfault.com/a/1190000005988895)\n	* 雪崩效应原因：硬件故障、硬件故障、程序Bug、重试加大流量、用户大量请求。 \n	* 雪崩的对策：限流、改进缓存模式(缓存预加载、同步调用改异步)、自动扩容、降级。\n	* Hystrix设计原则：\n		* 资源隔离：Hystrix通过将每个依赖服务分配独立的线程池进行资源隔离, 从而避免服务雪崩。\n		* 熔断开关：服务的健康状况 = 请求失败数 / 请求总数，通过阈值设定和滑动窗口控制开关。\n		* 命令模式：通过继承 HystrixCommand 来包装服务调用逻辑。 \n\n* [《缓存穿透，缓存击穿，缓存雪崩解决方案分析》](https://blog.csdn.net/zeb_perfect/article/details/54135506)\n* [《缓存击穿、失效以及热点key问题》](https://blog.csdn.net/zeb_perfect/article/details/54135506) \n	* 主要策略：失效瞬间：单机使用锁；使用分布式锁；不过期；\n	* 热点数据：热点数据单独存储；使用本地缓存；分成多个子key；\n\n### 跨机房容灾\n\n* [《“异地多活”多机房部署经验谈》](http://dc.idcquan.com/ywgl/71559.shtml)\n	* 通过自研中间件进行数据同步。 \n\n* [《异地多活（异地双活）实践经验》](https://blog.csdn.net/jeffreynicole/article/details/48135093)\n	* 注意延迟问题，多次跨机房调用会将延时放大数倍。\n	* 建房间专线很大概率会出现问题，做好运维和程序层面的容错。\n	* 不能依赖于程序端数据双写，要有自动同步方案。 \n	* 数据永不在高延迟和较差网络质量下，考虑同步质量问题。\n	* 核心业务和次要业务分而治之，甚至只考虑核心业务。\n	* 异地多活监控部署、测试也要跟上。\n	* 业务允许的情况下考虑用户分区，尤其是游戏、邮箱业务。\n	* 控制跨机房消息体大小，越小越好。\n	* 考虑使用docker容器虚拟化技术，提高动态调度能力。\n\n* [容灾技术及建设经验介绍](https://blog.csdn.net/yoara/article/details/38013751)\n\n\n### 容灾演练流程\n\n* [《依赖治理、灰度发布、故障演练，阿里电商故障演练系统的设计与实战经验》](https://mp.weixin.qq.com/s?__biz=MjM5MDE0Mjc4MA==&mid=2650996320&idx=1&sn=0ed3be190bbee4a9277886ef88cbb2e5)\n	* 常见故障画像\n	* 案例：预案有效性、预案有效性、故障复现、架构容灾测试、参数调优、参数调优、故障突袭、联合演练。\n\n### 平滑启动\n\n* 平滑重启应用思路\n1.端流量（如vip层）、2. flush 数据(如果有)、3, 重启应用\n\n* [《JVM安全退出（如何优雅的关闭java服务）》](https://blog.csdn.net/u011001084/article/details/73480432)\n推荐推出方式：System.exit，Kill SIGTERM；不推荐 kill-9；用 Runtime.addShutdownHook 注册钩子。\n* [《常见Java应用如何优雅关闭》](http://ju.outofmemory.cn/entry/337235)\nJava、Spring、Dubbo 优雅关闭方式。\n\n## 数据库扩展\n\n### 读写分离模式\n\n* [《Mysql主从方案的实现》](https://www.cnblogs.com/houdj/p/6563771.html)\n* [《搭建MySQL主从复制经典架构》](https://www.cnblogs.com/edisonchou/p/4133148.html)\n* [《Haproxy+多台MySQL从服务器(Slave) 实现负载均衡》](https://blog.csdn.net/nimasike/article/details/48048341)\n\n* [《DRBD+Heartbeat+Mysql高可用读写分离架构》](https://www.cnblogs.com/zhangsubai/p/6801764.html)\n	* DRDB 进行磁盘复制，避免单点问题。\n\n* [《MySQL Cluster 方式》](https://coderxing.gitbooks.io/architecture-evolution/di-san-pian-ff1a-bu-luo/62-ke-kuo-zhan-de-shu-ju-ku-jia-gou/621-gao-ke-yong-mysql-de-ji-zhong-fang-an/6214-mysql-cluster-fang-an.html)\n\n### 分片模式\n* [《分库分表需要考虑的问题及方案》](https://www.jianshu.com/p/32b3e91aa22c)\n	* 中间件： 轻量级：sharding-jdbc、TSharding；重量级：Atlas、MyCAT、Vitess等。\n	* 问题：事务、Join、迁移、扩容、ID、分页等。\n	* 事务补偿：对数据进行对帐检查;基于日志进行比对;定期同标准数据来源进行同步等。\n	* 分库策略：数值范围；取模；日期等。\n	* 分库数量：通常 MySQL 单库 5千万条、Oracle 单库一亿条需要分库。 \n\n* [《MySql分表和表分区详解》](https://www.2cto.com/database/201503/380348.html)\n	* 分区：是MySQL内部机制，对客户端透明，数据存储在不同文件中，表面上看是同一个表。\n	* 分表：物理上创建不同的表、客户端需要管理分表路由。\n\n## 服务治理\n###  服务注册与发现\n\n* [《永不失联！如何实现微服务架构中的服务发现？》](https://blog.csdn.net/jiaolongdy/article/details/51188798)\n  * 客户端服务发现模式：客户端直接查询注册表，同时自己负责负载均衡。Eureka 采用这种方式。\n  * 服务器端服务发现模式：客户端通过负载均衡查询服务实例。\n* [《SpringCloud服务注册中心比较:Consul vs Zookeeper vs Etcd vs Eureka》](https://blog.csdn.net/u010963948/article/details/71730165)\n  * CAP支持：Consul（CA）、zookeeper（cp）、etcd（cp） 、euerka（ap）\n  * 作者认为目前 Consul 对 Spring cloud 的支持比较好。\n\n* [《基于Zookeeper的服务注册与发现》](http://mobile.51cto.com/news-502394.htm)\n	* 优点：API简单、Pinterest，Airbnb 在用、多语言、通过watcher机制来实现配置PUSH，能快速响应配置变化。 \n\n### 服务路由控制\n* [《分布式服务框架学习笔记4 服务路由》](https://blog.csdn.net/xundh/article/details/59492750)\n	* 原则：透明化路由\n	* 负载均衡策略：随机、轮询、服务调用延迟、一致性哈希、粘滞连接\n	* 本地路由有限策略：injvm(优先调用jvm内部的服务)，innative(优先使用相同物理机的服务),原则上找距离最近的服务。\n	* 配置方式：统一注册表；本地配置；动态下发。\n\n## 分布式一致\n\n### CAP 与 BASE 理论\n\n* [《从分布式一致性谈到CAP理论、BASE理论》](http://www.cnblogs.com/szlbm/p/5588543.html)\n	* 一致性分类：强一致(立即一致)；弱一致(可在单位时间内实现一致，比如秒级)；最终一致(弱一致的一种，一定时间内最终一致)\n	* CAP：一致性、可用性、分区容错性(网络故障引起)\n	* BASE：Basically Available（基本可用）、Soft state（软状态）和Eventually consistent（最终一致性）\n	* BASE理论的核心思想是：即使无法做到强一致性，但每个应用都可以根据自身业务特点，采用适当的方式来使系统达到最终一致性。\n\n### 分布式锁\n\n* [《分布式锁的几种实现方式》](http://www.hollischuang.com/archives/1716)\n	* 基于数据库的分布式锁：优点：操作简单、容易理解。缺点：存在单点问题、数据库性能够开销较大、不可重入；\n	* 基于缓存的分布式锁：优点：非阻塞、性能好。缺点：操作不好容易造成锁无法释放的情况。\n	* Zookeeper 分布式锁：通过有序临时节点实现锁机制，自己对应的节点需要最小，则被认为是获得了锁。优点：集群可以透明解决单点问题，避免锁不被释放问题，同时锁可以重入。缺点：性能不如缓存方式，吞吐量会随着zk集群规模变大而下降。\n* [《基于Zookeeper的分布式锁》](https://www.tuicool.com/articles/VZJr6fY)\n	* 清楚的原理描述 + Java 代码示例。 \n\n* [《jedisLock—redis分布式锁实现》](https://www.cnblogs.com/0201zcr/p/5942748.html)\n	* 基于 setnx(set if ont exists)，有则返回false，否则返回true。并支持过期时间。\n\n* [《Memcached 和 Redis 分布式锁方案》](https://blog.csdn.net/albertfly/article/details/77412333)\n	* 利用 memcached 的 add（有别于set）操作，当key存在时，返回false。\n\n### 分布式一致性算法\n\n#### PAXOS\n* [《分布式系列文章——Paxos算法原理与推导》](https://www.cnblogs.com/linbingdong/p/6253479.html)\n* [《Paxos-->Fast Paxos-->Zookeeper分析》](https://blog.csdn.net/u010039929/article/details/70171672)\n* [《【分布式】Zookeeper与Paxos》](https://www.cnblogs.com/leesf456/p/6012777.html)\n\n#### Zab\n* [《Zab：Zookeeper 中的分布式一致性协议介绍》](https://www.jianshu.com/p/fb527a64deee)\n\n#### Raft\n* [《Raft 为什么是更易理解的分布式一致性算法》](http://www.cnblogs.com/mindwind/p/5231986.html)\n	* 三种角色：Leader（领袖）、Follower（群众）、Candidate（候选人）\n	* 通过随机等待的方式发出投票，得票多的获胜。\n\n#### Gossip\n* [《Gossip算法》](http://blog.51cto.com/tianya23/530743)\n\n#### 两阶段提交、多阶段提交\n\n* [《关于分布式事务、两阶段提交协议、三阶提交协议》](http://blog.jobbole.com/95632/)\n\n### 幂等\n\n* [《分布式系统---幂等性设计》](https://www.cnblogs.com/wxgblogs/p/6639272.html)\n	* 幂等特性的作用：该资源具备幂等性，请求方无需担心重复调用会产生错误。\n	* 常见保证幂等的手段：MVCC（类似于乐观锁）、去重表(唯一索引)、悲观锁、一次性token、序列号方式。 \n\n### 分布式一致方案\n* [《分布式系统事务一致性解决方案》](http://www.infoq.com/cn/articles/solution-of-distributed-system-transaction-consistency)\n* [《保证分布式系统数据一致性的6种方案》](https://weibo.com/ttarticle/p/show?id=2309403965965003062676)\n\n### 分布式 Leader 节点选举\n* [《利用zookeeper实现分布式leader节点选举》](https://blog.csdn.net/johnson_moon/article/details/78809995)\n\n### TCC(Try/Confirm/Cancel) 柔性事务\n* [《传统事务与柔性事务》](https://www.jianshu.com/p/ab1a1c6b08a1)\n	* 基于BASE理论：基本可用、柔性状态、最终一致。\n	* 解决方案：记录日志+补偿（正向补充或者回滚）、消息重试(要求程序要幂等)；“无锁设计”、采用乐观锁机制。\n\n## 分布式文件系统\n\n* [说说分布式文件存储系统-基本架构](https://zhuanlan.zhihu.com/p/27666295) ？\n* [《各种分布式文件系统的比较》](https://blog.csdn.net/gatieme/article/details/44982961) ？\n  * HDFS：大批量数据读写，用于高吞吐量的场景，不适合小文件。\n  * FastDFS：轻量级、适合小文件。\n\n## 唯一ID 生成\n\n### 全局唯一ID\n* [《高并发分布式系统中生成全局唯一Id汇总》](https://www.cnblogs.com/baiwa/p/5318432.html)\n	* Twitter 方案（Snowflake 算法）：41位时间戳+10位机器标识（比如IP，服务器名称等）+12位序列号(本地计数器)\n	* Flicker 方案：MySQL自增ID + \"REPLACE INTO XXX:SELECT LAST_INSERT_ID();\" \n	* UUID：缺点，无序，字符串过长，占用空间，影响检索性能。\n	* MongoDB 方案：利用 ObjectId。缺点：不能自增。\n\n* [《TDDL 在分布式下的SEQUENCE原理》](https://blog.csdn.net/hdu09075340/article/details/79103851)\n	* 在数据库中创建 sequence 表，用于记录，当前已被占用的id最大值。\n	* 每台客户端主机取一个id区间（比如 1000~2000）缓存在本地，并更新 sequence 表中的id最大值记录。\n	* 客户端主机之间取不同的id区间，用完再取，使用乐观锁机制控制并发。\n\n## 一致性Hash算法\n\n* [《一致性哈希算法》](https://coderxing.gitbooks.io/architecture-evolution/di-san-pian-ff1a-bu-luo/631-yi-zhi-xing-ha-xi.html)\n\n# 设计思想 & 开发模式\n\n## DDD(Domain-driven Design - 领域驱动设计)\n\n* [《浅谈我对DDD领域驱动设计的理解》](https://www.cnblogs.com/netfocus/p/5548025.html)\n  * 概念：DDD 主要对传统软件开发流程(分析-设计-编码)中各阶段的割裂问题而提出，避免由于一开始分析不明或在软件开发过程中的信息流转不一致而造成软件无法交付（和需求方设想不一致）的问题。DDD 强调一切以领域（Domain）为中心，强调领域专家（Domain Expert）的作用，强调先定义好领域模型之后在进行开发，并且领域模型可以指导开发（所谓的驱动）。\n  * 过程：理解领域、拆分领域、细化领域，模型的准确性取决于模型的理解深度。\n  * 设计：DDD 中提出了建模工具，比如聚合、实体、值对象、工厂、仓储、领域服务、领域事件来帮助领域建模。\n  \n* [《领域驱动设计的基础知识总结》](https://www.cnblogs.com/butterfly100/p/7827870.html)\n  * 领域（Doamin）本质上就是问题域，比如一个电商系统，一个论坛系统等。\n  * 界限上下文（Bounded Context）：阐述子域之间的关系，可以简单理解成一个子系统或组件模块。\n  * 领域模型（Domain Model）：DDD的核心是建立（用通用描述语言、工具—领域通用语言）正确的领域模型；反应业务需求的本质，包括实体和过程；其贯穿软件分析、设计、开发 的整个过程；常用表达领域模型的方式：图、代码或文字；\n  * 领域通用语言：领域专家、开发设计人员都能立即的语言或工具。\n  * 经典分层架构：用户界面/展示层、应用层、领域层、基础设施层，是四层架构模式。\n  * 使用的模式：\n    * 关联尽量少，尽量单项，尽量降低整体复杂度。\n    * 实体（Entity）：领域中的唯一标示，一个实体的属性尽量少，少则清晰。\n    * 值对象（Value Object）：没有唯一标识，且属性值不可变，小二简单的对象，比如Date。\n    * 领域服务（Domain Service）： 协调多个领域对象，只有方法没有状态(不存数据)；可以分为应用层服务，领域层服务、基础层服务。\n    * 聚合及聚合根（Aggregate，Aggregate Root）：聚合定义了一组具有内聚关系的相关对象的集合；聚合根是对聚合引用的唯一元素；当修改一个聚合时，必须在事务级别；大部分领域模型中，有70%的聚合通常只有一个实体，30%只有2~3个实体；如果一个聚合只有一个实体，那么这个实体就是聚合根；如果有多个实体，那么我们可以思考聚合内哪个对象有独立存在的意义并且可以和外部直接进行交互；\n    * 工厂（Factory）：类似于设计模式中的工厂模式。\n    * 仓储（Repository）：持久化到DB，管理对象，且只对聚合设计仓储。\n\n* [《领域驱动设计(DDD)实现之路》](http://www.cnblogs.com/Leo_wl/p/3866629.html)\n	* 聚合：比如一辆汽车（Car）包含了引擎（Engine）、车轮（Wheel）和油箱（Tank）等组件，缺一不可。\n\n* [《领域驱动设计系列（2）浅析VO、DTO、DO、PO的概念、区别和用处》](http://www.hollischuang.com/archives/553)\n\n\n### 命令查询职责分离(CQRS)\n\nCQRS — Command Query Responsibility Seperation\n\n* [《领域驱动设计系列 (六)：CQRS》](https://www.cnblogs.com/cnblogsfans/p/4551990.html)\n	* 核心思想：读写分离（查询和更新在不同的方法中），不同的流程只是不同的设计方式，CQ代码分离，分布式环境中会有明显体现（有冗余数据的情况下），目的是为了高性能。\n\n* [《DDD CQRS架构和传统架构的优缺点比较》](http://www.techweb.com.cn/network/system/2017-07-07/2553563.shtml)\n	* 最终一致的设计理念；依赖于高可用消息中间件。\n	\n* [《CQRS架构简介》](http://www.cnblogs.com/netfocus/p/4055346.html)\n	* 一个实现 CQRS 的抽象案例。\n\n* [《深度长文：我对CQRS/EventSourcing架构的思考》](http://www.uml.org.cn/zjjs/201609221.asp)\n	* CQRS 模式分析 + 12306 抢票案例\n\n### 贫血，充血模型\n\n* [《贫血，充血模型的解释以及一些经验》](https://kb.cnblogs.com/page/520743/)\n	* 失血模型：老子和儿子分别定义，相互不知道，二者实体定义中完全没有业务逻辑，通过外部Service进行关联。\n	* 贫血模型：老子知道儿子，儿子也知道老子；部分业务逻辑放到实体中；优点：各层单项依赖，结构清楚，易于维护；缺点：不符合OO思想，相比于充血模式，Service层较为厚重；\n	* 充血模型：和贫血模型类似，区别在于如何划分业务逻辑。优点：Service层比较薄，只充当Facade的角色，不和DAO打交道、复合OO思想；缺点：非单项依赖，DO和DAO之间双向依赖、和Service层的逻辑划分容易造成混乱。\n	* 肿胀模式：是一种极端情况，取消Service层、全部业务逻辑放在DO中；优点：符合OO思想、简化了分层；缺点：暴露信息过多、很多非DO逻辑也会强行并入DO。这种模式应该避免。\n	* 作者主张使用贫血模式。\n	\n## Actor 模式\n\nTODO\n\n## 响应式编程\n\n### Reactor\nTODO\n### RxJava\nTODO\n### Vert.x\nTODO\n\n## DODAF2.0\n\n* [《DODAF2.0方法论》](http://www.360doc.com/content/16/0627/19/33945750_571201779.shtml)\n* [《DODAF2.0之能力视角如何落地》](http://blog.51cto.com/xiaoyong/1553164)\n\n## Serverless\n\n无需过多关系服务器的服务架构理念。\n\n* [《什么是Serverless无服务器架构？》](http://www.jdon.com/soa/serverless.html)\n	* Serverless 不代表出去服务器，而是去除对服务器运行状态的关心。\n	* Serverless 代表一思维方式的转变，从“构建一套服务在一台服务器上，对对个事件进行响应转变为构建一个为服务器，来响应一个事件”。\n	* Serverless 不代表某个具体的框架。\n\n* [《如何理解Serverless？》](http://www.infoq.com/cn/news/2017/10/how-to-understand-serverless)\n	* 依赖于 Baas （(Mobile) Backend as a Service） 和 Faas （Functions as a service）\n\n\n\n## Service Mesh\n\n* [《什么是Service Mesh？》](https://time.geekbang.org/article/2355)\n* [《初识 Service Mesh》](https://www.jianshu.com/p/e23e3e74538e)\n\n\n# 项目管理\n\n## 架构评审\n* [《架构设计之如何评审架构设计说明书》](http://developer.51cto.com/art/201506/478486.htm)\n* [《人人都是架构师：非功能性需求》](https://blog.csdn.net/wireless_com/article/details/45935591)\n\n## 重构\n\n* [《架构之重构的12条军规》](http://www.infoq.com/cn/articles/architect-12-rules-complete/)\n\n## 代码规范\n\n* [《阿里巴巴Java开发手册》](https://github.com/alibaba/p3c)\n\n## 代码 Review\n\n\n制度还是制度!\n另外，每个公司需要根据自己的需求和目标制定自己的 check list\n\n* [《为什么你做不好 Code Review？》](http://www.sohu.com/a/229745352_181657)\n	* 代码 review 做的好，在于制度建设。\n\n* [《从零开始Code Review》](https://blog.csdn.net/uxyheaven/article/details/49773619)\n\n* [《Code Review Checklist》](https://www.cnblogs.com/zuoping/p/5477047.html)\n* [《Java Code Review Checklist》](https://dzone.com/articles/java-code-review-checklist)\n\n* [《如何用 gitlab 做 code review》](https://blog.csdn.net/legend0011/article/details/45585575)\n\n## RUP\n* [《运用RUP 4+1视图方法进行软件架构设计》](https://blog.csdn.net/apanious/article/details/51011946)\n\n## 看板管理\n* [《说说看板在项目中的应用》](https://blog.csdn.net/tkchen/article/details/51637643)\n\n## SCRUM\n\nSCRUM - 争球\n\n* 3个角色:Product Owner(PO) 产品负责人;Scrum Master（SM），推动Scrum执行;Team 开发团队。\n* 3个工件：Product Backlog 产品TODOLIST，含优先级;Sprint Backlog 功能开发 TODO LIST；燃尽图；\n* 五个价值观：专注、勇气、公开、承诺、尊重。\n\n\n* [《敏捷项目管理流程-Scrum框架最全总结！》](https://blog.csdn.net/inny100_100/article/details/54633757)\n\n* [《敏捷其实很简单3---敏捷方法之scrum》](https://blog.csdn.net/superkunkun/article/details/52951142)\n\n## 敏捷开发\n\nTODO\n\n## 极限编程（XP）\n\nXP - eXtreme Programming\n\n* [《主流敏捷开发方法：极限编程XP》](http://www.woshipm.com/pmd/406917.html)\n	* 是一种指导开发人员的方法论。\n	* 4大价值：\n		* 沟通：鼓励口头沟通，提高效率。\n		* 简单：够用就好。\n		* 反馈：及时反馈、通知相关人。\n		* 勇气：提倡拥抱变化，敢于重构。\n		\n	* 5个原则：快速反馈、简单性假设、逐步修改、提倡更改（小步快跑）、优质工作（保证质量的前提下保证小步快跑）。\n	* 5个工作：阶段性冲刺；冲刺计划会议；每日站立会议；冲刺后review；回顾会议。\n\n## 结对编程\n\n边写码，边review。能够增强代码质量、减少bug。\n\n* [《结对编程》](http://www.baike.com/wiki/%E7%BB%93%E5%AF%B9%E7%BC%96%E7%A8%8B)\n\n## PDCA 循环质量管理\n\nP——PLAN 策划，D——DO 实施，C——CHECK 检查，A——ACT 改进\n\n* [《PDCA》](http://www.baike.com/wiki/PDCA)\n\n## FMEA管理模式\n\nTODO\n\n# 通用业务术语\n\nTODO\n\n# 技术趋势\n\nTODO\n\n# 政策、法规\n\nTODO\n## 法律\n\n### 严格遵守刑法253法条\n\n我国刑法第253条之一规定：\n\n> * 国家机关或者金融、电信、交通、教育、医疗等单位的工作人员，违反国家规定，将本单位在履行职责或者提供服务过程中获得的公民个人信息，出售或者非法提供给他人，情节严重的，处3年以下有期徒刑或者拘役，并处或者单处罚金。\n> * 窃取或者以其他方法非法获取上述信息，情节严重的，依照前款的规定处罚。 \n> * 单位犯前两款罪的，对单位判处罚金，并对其直接负责的主管人员和其他直接责任人员，依照各该款的规定处罚。\n\n最高人民法院、最高人民检察院关于执行《中华人民共和国刑法》确定罪名的补充规定（四）规定：触犯刑法第253条之一第1款之规定，构成“出售、非法提供公民个人信息罪”；触犯刑法第253条之一第2款之规定，构成“非法获取公民个人信息罪”\n\n* [《非法获取公民个人信息罪》](https://baike.baidu.com/item/%E9%9D%9E%E6%B3%95%E8%8E%B7%E5%8F%96%E5%85%AC%E6%B0%91%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF%E7%BD%AA)\n\n\n# 架构师素质\n\n* [《架构师画像》](http://hellojava.info/?p=430)\n	* 业务理解和抽象能力\n	* NB的代码能力\n	* 全面：1. 在面对业务问题上，架构师脑海里是否会浮现出多种技术方案；2. 在做系统设计时是否考虑到了足够多的方方面面；3. 在做系统设计时是否考虑到了足够多的方方面面； \n	* 全局：是否考虑到了对上下游的系统的影响。\n	* 权衡：权衡投入产出比；优先级和节奏控制；\n\n* [《关于架构优化和设计，架构师必须知道的事情》](http://www.infoq.com/cn/articles/architecture-optimization-and-design-the-architect-must-know)\n	* 要去考虑的细节：模块化、轻耦合、无共享架构；减少各个组件之前的依赖、注意服务之间依赖所有造成的链式失败及影响等。\n	* 基础设施、配置、测试、开发、运维综合考虑。\n	* 考虑人、团队、和组织的影响。 \n\n* [《如何才能真正的提高自己，成为一名出色的架构师？》](https://www.zhihu.com/question/19841397) \n\n* [《架构师的必备素质和成长途径》](https://blog.csdn.net/sanbingyutuoniao123/article/details/54144129)\n	* 素质：业务理解、技术广度、技术深度、丰富经验、沟通能力、动手能力、美学素养。\n	* 成长路径：2年积累知识、4年积累技能和组内影响力、7年积累部门内影响力、7年以上积累跨部门影响力。 \n\n* [《架构设计师—你在哪层楼？》](http://blog.51cto.com/frankfan/1248401)\n	* 第一层的架构师看到的只是产品本身\n	* 第二层的架构师不仅看到自己的产品，还看到了整体的方案\n	* 第三层的架构师看到的是商业价值 \n\n# 团队管理\n\nTODO\n\n## 招聘\n\n# 资讯\n\n## 行业资讯\n\n* [36kr](http://36kr.com/)\n* [Techweb](http://www.techweb.com.cn/)\n\n## 公众号列表\n\nTODO\n\n## 博客\n\n### 团队博客\n* [阿里中间件博客](http://jm.taobao.org/)\n* [美团点评技术团队博客](https://tech.meituan.com)\n\n### 个人博客\n\n* [阮一峰的网络日志](http://www.ruanyifeng.com/)\n* [酷壳 - COOLSHELL-陈皓](https://coolshell.cn/)\n* [hellojava-阿里毕玄](http://hellojava.info/)\n* [Cm\'s Blog](http://cmsblogs.com/)\n* [程序猿DD-翟永超-《Spring Cloud微服务实战》作者](http://blog.didispace.com/)\n\n## 综合门户、社区\n\n**国内：**\n\n* [CSDN](http://csdn.net)\n	 老牌技术社区、不必解释。\n* [51cto.com](http://www.51cto.com/)\n* [ITeye](http://www.iteye.com/)\n	* 偏 Java 方向 \n* [博客园](https://www.cnblogs.com)\n* [ChinaUnix](http://www.tom.net/)\n	* 偏 Linux 方向 \n* [开源中国社区](https://www.oschina.net/)\n* [深度开源](http://www.open-open.com/)\n* [伯乐在线](http://www.jobbole.com/)\n	* 涵盖 IT职场、Web前端、后端、移动端、数据库等方面内容，偏技术端。\n\n* [ITPUB](http://www.itpub.net/)\n* [腾讯云— 云+社区](https://cloud.tencent.com/developer/column)\n* [阿里云— 云栖社区](https://yq.aliyun.com/)\n* [IBM DeveloperWorks](https://www.ibm.com/developerworks/cn/)\n* [开发者头条](https://toutiao.io/)\n* [LinkedKeeper](http://www.linkedkeeper.com)\n\n**国外：**\n\n* [DZone](https://dzone.com)\n* [Reddit](https://www.reddit.com)\n\n## 问答、讨论类社区\n\n* [segmentfault](https://segmentfault.com)\n	* 问答+专栏 \n* [知乎](https://www.zhihu.com/)\n* [stackoverflow](https://stackoverflow.com/)\n\n## 行业数据分析\n\n* [艾瑞网](http://report.iresearch.cn/)\n* [QUEST MOBILE](https://www.questmobile.com.cn)\n\n* [国家数据](http://data.stats.gov.cn/)\n\n* [TalkingData](http://www.talkingdata.com/)\n\n## 专项网站\n\n* 测试:\n	* [领测国际](http://www.ltesting.net/) \n	* [测试窝](https://www.testwo.com/)\n	* [TesterHome](https://testerhome.com)\n\n* 运维:\n  	* [运维派](http://www.yunweipai.com/) \n  	* [Abcdocker](https://www.abcdocker.com/)\n  \n* Java:\n	* [ImportNew](http://www.importnew.com/)\n		* 专注于 Java 技术分享\n	* [HowToDoInJava](https://howtodoinjava.com/)\n		* 英文博客\n	\n* 安全\n	* [红黑联盟](https://www.2cto.com/) \n	* [FreeBuf](http://www.freebuf.com/)\n\n* 大数据\n	* [中国大数据](http://www.thebigdata.cn/) \n\n* 其他专题网站：\n	* [InfoQ](http://www.infoq.com/cn/)\n		* 偏重于基础架构、运维方向\n	* [DockerInfo](http://www.dockerinfo.net/)\n		* 专注于 Docker 应用及咨询、教程的网站\n	* [Linux公社](https://www.linuxidc.com/)\n		* Linux 主题社区\n\n## 其他类\n\n* [程序员技能图谱](https://github.com/TeamStuQ/skill-map)\n\n## 推荐参考书\n\n\n### 在线电子书\n\n* [《深入理解Spring Cloud与微服务构建》](https://github.com/forezp/SpringCloudLearning)\n\n\n* [《阿里技术参考图册-研发篇》](http://techforum-img.cn-hangzhou.oss-pub.aliyun-inc.com/1523849261680/AliTech101_RD.pdf)\n* [《阿里技术参考图册-算法篇》](http://techforum-img.cn-hangzhou.oss-pub.aliyun-inc.com/1523848064814/AliTech101_Algorithms.pdf)\n\n* [《2018美团点评技术年货（合辑）》70M](http://dpurl.cn/n/1lqcX)\n\n* [InfoQ《架构师》月刊](http://www.infoq.com/cn/architect/)\n\n* [《架构师之路》](https://www.w3cschool.cn/architectroad/)\n\n### 纸质书\n\n#### 开发方面\n\n* 《阿里巴巴Java开发手册》[详情](https://www.coderxing.com/r.php?r=https://union-click.jd.com/jdc?d=BfL5CR)\n\n#### 架构方面\n* 《软件架构师的12项修炼：技术技能篇》[详情](https://www.coderxing.com/r.php?r=https://union-click.jd.com/jdc?d=rTlo0m)\n* 《架构之美》[详情](https://www.coderxing.com/r.php?r=https://union-click.jd.com/jdc?d=1KECBZ) \n* 《分布式服务架构》[详情](https://www.coderxing.com/r.php?r=https://union-click.jd.com/jdc?d=hkzqtK) \n* 《聊聊架构》 [详情](https://www.coderxing.com/r.php?r=https://union-click.jd.com/jdc?d=A8Nd6Z) \n* 《云原生应用架构实践》[详情](https://www.coderxing.com/r.php?r=https://union-click.jd.com/jdc?d=D4WCpd) \n* 《亿级流量网站架构核心技术》[详情](https://www.coderxing.com/r.php?r=https://union-click.jd.com/jdc?d=Rdmd21)\n* 《淘宝技术这十年》[详情](https://www.coderxing.com/r.php?r=https://union-click.jd.com/jdc?d=CoUdGG)\n* 《企业IT架构转型之道-中台战略思想与架构实战》 [详情](https://www.coderxing.com/r.php?r=https://union-click.jd.com/jdc?d=BxS6eI) \n\n* 《高可用架构（第1卷）》[详情](https://www.coderxing.com/r.php?r=https://union-click.jd.com/jdc?d=BcjUwS) \n\n#### 技术管理方面\n* 《CTO说》[详情](https://www.coderxing.com/r.php?r=https://union-click.jd.com/jdc?d=Gl3QAo) \n* 《技术管理之巅》[详情](https://www.coderxing.com/r.php?r=https://union-click.jd.com/jdc?d=MeloLt)\n* 《网易一千零一夜：互联网产品项目管理实战》[详情](https://www.coderxing.com/r.php?r=https://union-click.jd.com/jdc?d=qPuqMg) \n\n#### 基础理论\n* 《数学之美》[详情](https://www.coderxing.com/r.php?r=https://union-click.jd.com/jdc?d=0seUpO) \n* 《编程珠玑》[详情](https://www.coderxing.com/r.php?r=https://union-click.jd.com/jdc?d=I7jj9r) \n\n#### 工具方面\nTODO\n\n#### 大数据方面\n\n# 技术资源\n\n## 开源资源\n* [github](https://github.com)\n\n* [Apache 软件基金会](https://www.apache.org/index.html)\n\n## 手册、文档、教程\n\n**国内：**\n* [W3Cschool](http://w3cschool.cn) \n* [Runoob.com](http://www.runoob.com/)\n	* HTML 、 CSS、XML、Java、Python、PHP、设计模式等入门手册。\n\n* [Love2.io](https://love2.io/)\n	* 很多很多中文在线电子书，是一个全新的开源技术文档分享平台。\n* [gitbook.cn](http://gitbook.cn/)\n	* 付费电子书。 \n* [ApacheCN](http://www.apachecn.org/)\n	* AI、大数据方面系列中文文档。\n\n**国外：**\n\n* [Quick Code](http://www.quickcode.co/)\n	* 免费在线技术教程。\n* [gitbook.com](http://gitbook.com)\n	* 有部分中文电子书。\n* [Cheatography](https://www.cheatography.com/)\n	* Cheat Sheets 大全，单页文档网站。\n* [Tutorialspoint](https://www.tutorialspoint.com/index.htm)\n	* 知名教程网站，提供Java、Python、JS、SQL、大数据等高质量入门教程。\n\n\n## 在线课堂\n\n* [学徒无忧](http://www.xuetuwuyou.com/)\n* [极客时间](https://time.geekbang.org/)\n* [segmentfault](https://segmentfault.com/lives)\n* [斯达克学院](https://new.stuq.org/course/explore)\n* [牛客网](http://nowcoder.com)\n* [极客学院](https://www.jikexueyuan.com/)\n* [51CTO学院](http://edu.51cto.com/)\n\n## 会议、活动\n\n* [QCon](http://www.infoq.com/cn/qcon/)\n* [ArchSummit](https://archsummit.com)\n* [GITC全球互联网技术大会](http://www.thegitc.com/)\n\n**活动发布平台:** \n* [活动行](http://www.huodongxing.com/)\n\n## 常用APP\n\n* [极客时间](https://time.geekbang.org)\n* [得到](https://www.igetget.com)\n\n## 找工作\n* [Boss直聘](https://www.zhipin.com)\n* [拉勾网](https://www.lagou.com)\n* [猎聘](https://www.liepin.com)\n* [100Offer](https://cn.100offer.com/)\n\n## 工具\n\n* [极客搜索](https://s.geekbang.org/)\n	* 技术文章搜索引擎。\n\n## 代码托管\n\n* [Coding](https://coding.net)\n* [码云](https://gitee.com/)\n\n## 文件服务\n* 七牛\n* 又拍云\n\n## 综合云服务商\n* 阿里云\n* [腾讯云](https://cloud.tencent.com/redirect.php?redirect=1012&cps_key=c2665015d90871c0cb20fef91b7afc3c)\n* 百度云\n* 新浪云\n* 金山云\n* [亚马逊云(AWS)](https://amazonaws-china.com/cn/)\n* [谷歌云](https://cloud.google.com/?hl=zh-cn)\n* [微软云](https://azure.microsoft.com/zh-cn/)\n\n### VPS\n* [Linode](http://linode.com)\n* [DigitalOcean](https://www.digitalocean.com)\n* [Vultr](https://www.vultr.com/)\n\n\n', '后端架构师技术图谱', '1', 'Coriger', '2018-10-08 23:06:30', '2018-10-09 22:12:57', '812');
INSERT INTO `article` VALUES ('253831', '10001', 'Spring Boot启动流程详解', '代码如下：\n\n\n@SpringBootApplication\npublic class Application {\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n}\n\n@RestController\npublic class RootController {\n\n    public static final String PATH_ROOT = \"/\";\n\n    @RequestMapping(PATH_ROOT)\n    public String welcome() {\n        return \"Welcome!\";\n    }\n\n}\n虽然只有几行代码，但是这已经是一个完整的Web程序，当访问url的path部分为\"/\"时，返回字符串\"Welcome!\"。\n\n首先是一个非常普通的java程序入口，一个符合约定的静态main方法。在这个main方法中，调用了SpringApplication的静态run方法，并将Application类对象和main方法的参数args作为参数传递了进去。\n\n然后是一个使用了两个Spring注解的RootController类，我们在main方法中，没有直接使用这个类。\n\n**SpringApplication类的静态run方法**\n\n	public static ConfigurableApplicationContext run(Object source, String... args) {\n    	return run(new Object[] { source }, args);\n	}\n\n	public static ConfigurableApplicationContext run(Object[] sources, String[] args) {\n    	return new SpringApplication(sources).run(args);\n	}\n\n在这个静态方法中，创建SpringApplication对象，并调用该对象的run方法。\n\n**构造SpringApplication对象**\n\n	public SpringApplication(Object... sources) {\n		initialize(sources);\n	}\n\n	private void initialize(Object[] sources) {\n		// 为成员变量sources赋值\n		if (sources != null && sources.length > 0) {\n			this.sources.addAll(Arrays.asList(sources));\n		}\n		this.webEnvironment = deduceWebEnvironment();\n		setInitializers((Collection) getSpringFactoriesInstances(\n				ApplicationContextInitializer.class));\n		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));\n		this.mainApplicationClass = deduceMainApplicationClass();\n	}\n构造函数中调用initialize方法，初始化SpringApplication对象的成员变量sources，webEnvironment，initializers，listeners，mainApplicationClass。sources的赋值比较简单，就是我们传给SpringApplication.run方法的参数。剩下的几个，我们依次来看看是怎么做的。\n\n首先是webEnvironment:\n\n	private boolean webEnvironment; \n\n	private static final String[] WEB_ENVIRONMENT_CLASSES = { \"javax.servlet.Servlet\",\n				\"org.springframework.web.context.ConfigurableWebApplicationContext\" };\n\n	private void initialize(Object[] sources) {\n		...\n			// 为成员变量webEnvironment赋值\n			this.webEnvironment = deduceWebEnvironment();\n		...\n	}\n\n	private boolean deduceWebEnvironment() {\n		for (String className : WEB_ENVIRONMENT_CLASSES) {\n			if (!ClassUtils.isPresent(className, null)) {\n				return false;\n			}\n		}\n		return true;\n	}\n可以看到webEnvironment是一个boolean，该成员变量用来表示当前应用程序是不是一个Web应用程序。那么怎么决定当前应用程序是否Web应用程序呢，是通过在classpath中查看是否存在WEB_ENVIRONMENT_CLASSES这个数组中所包含的类，如果存在那么当前程序即是一个Web应用程序，反之则不然。\n在本文的例子中webEnvironment的值为true。\n\n然后是initializers:\ninitializers成员变量，是一个ApplicationContextInitializer类型对象的集合。 顾名思义，ApplicationContextInitializer是一个可以用来初始化ApplicationContext的接口。\n\n	private List<ApplicationContextInitializer<?>> initializers;\n\n	private void initialize(Object[] sources) {\n		...\n		// 为成员变量initializers赋值\n		setInitializers((Collection) getSpringFactoriesInstances(\n				ApplicationContextInitializer.class));\n		...\n	}\n\n	public void setInitializers(\n			Collection<? extends ApplicationContextInitializer<?>> initializers) {\n		this.initializers = new ArrayList<ApplicationContextInitializer<?>>();\n		this.initializers.addAll(initializers);\n	}\n可以看到，关键是调用getSpringFactoriesInstances(ApplicationContextInitializer.class)，来获取ApplicationContextInitializer类型对象的列表。\n\n	private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {\n		return getSpringFactoriesInstances(type, new Class<?>[] {});\n	}\n\n	private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,\n			Class<?>[] parameterTypes, Object... args) {\n		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();\n		// Use names and ensure unique to protect against duplicates\n		Set<String> names = new LinkedHashSet<String>(\n				SpringFactoriesLoader.loadFactoryNames(type, classLoader));\n		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,\n				classLoader, args, names);\n		AnnotationAwareOrderComparator.sort(instances);\n		return instances;\n	}\n在该方法中，首先通过调用SpringFactoriesLoader.loadFactoryNames(type, classLoader)来获取所有Spring Factories的名字，然后调用createSpringFactoriesInstances方法根据读取到的名字创建对象。最后会将创建好的对象列表排序并返回。\n\n	以下代码摘自：org.springframework.core.io.support.SpringFactoriesLoader\n\n	public static final String FACTORIES_RESOURCE_LOCATION = \"META-INF/spring.factories\";\n\n	public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {\n		String factoryClassName = factoryClass.getName();\n		try {\n			Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :\n					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));\n			List<String> result = new ArrayList<String>();\n			while (urls.hasMoreElements()) {\n				URL url = urls.nextElement();\n				Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));\n				String factoryClassNames = properties.getProperty(factoryClassName);\n				result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));\n			}\n			return result;\n		}\n		catch (IOException ex) {\n			throw new IllegalArgumentException(\"Unable to load [\" + factoryClass.getName() +\n					\"] factories from location [\" + FACTORIES_RESOURCE_LOCATION + \"]\", ex);\n		}\n	}\n可以看到，是从一个名字叫spring.factories的资源文件中，读取key为org.springframework.context.ApplicationContextInitializer的value。而spring.factories的部分内容如下：\n\n\n	以下内容摘自spring-boot-1.3.3.RELEASE.jar中的资源文件META-INF/spring.factories\n\n	# Application Context Initializers\n	org.springframework.context.ApplicationContextInitializer=\\\n	org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\\\n	org.springframework.boot.context.ContextIdApplicationContextInitializer,\\\n	org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\\\n	org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer\n\n可以看到，最近的得到的，是ConfigurationWarningsApplicationContextInitializer，ContextIdApplicationContextInitializer，DelegatingApplicationContextInitializer，ServerPortInfoApplicationContextInitializer这四个类的名字。\n\n接下来会调用createSpringFactoriesInstances来创建ApplicationContextInitializer实例。\n\n\n	以下代码摘自：org.springframework.boot.SpringApplication\n\n	private <T> List<T> createSpringFactoriesInstances(Class<T> type,\n			Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,\n			Set<String> names) {\n		List<T> instances = new ArrayList<T>(names.size());\n		for (String name : names) {\n			try {\n				Class<?> instanceClass = ClassUtils.forName(name, classLoader);\n				Assert.isAssignable(type, instanceClass);\n				Constructor<?> constructor = instanceClass.getConstructor(parameterTypes);\n				T instance = (T) constructor.newInstance(args);\n				instances.add(instance);\n			}\n			catch (Throwable ex) {\n				throw new IllegalArgumentException(\n						\"Cannot instantiate \" + type + \" : \" + name, ex);\n			}\n		}\n		return instances;\n	}\n所以在我们的例子中，SpringApplication对象的成员变量initalizers就被初始化为，ConfigurationWarningsApplicationContextInitializer，ContextIdApplicationContextInitializer，DelegatingApplicationContextInitializer，ServerPortInfoApplicationContextInitializer这四个类的对象组成的list。\n\n下图画出了加载的ApplicationContextInitializer，并说明了他们的作用。至于何时应用他们，且听后面慢慢分解。\n\n[![](http://zhaox.github.io/assets/images/SpringBootApplicationContextInitializer.png)](http://zhaox.github.io/assets/images/SpringBootApplicationContextInitializer.png)\n接下来是成员变量listeners\n\n	以下代码摘自：org.springframework.boot.SpringApplication\n\n	private List<ApplicationListener<?>> listeners;\n\n	private void initialize(Object[] sources) {\n		...\n		// 为成员变量listeners赋值\n		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));\n		...\n	}\n\n	public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {\n		this.listeners = new ArrayList<ApplicationListener<?>>();\n		this.listeners.addAll(listeners);\n	}\nlisteners成员变量，是一个ApplicationListener<?>类型对象的集合。可以看到获取该成员变量内容使用的是跟成员变量initializers一样的方法，只不过传入的类型从ApplicationContextInitializer.class变成了ApplicationListener.class。\n\n看一下spring.factories中的相关内容：\n\n\n	以下内容摘自spring-boot-1.3.3.RELEASE.jar中的资源文件META-INF/spring.factories\n\n	# Application Listeners\n	org.springframework.context.ApplicationListener=\\\n	org.springframework.boot.builder.ParentContextCloserApplicationListener,\\\n	org.springframework.boot.context.FileEncodingApplicationListener,\\\n	org.springframework.boot.context.config.AnsiOutputApplicationListener,\\\n	org.springframework.boot.context.config.ConfigFileApplicationListener,\\\n	org.springframework.boot.context.config.DelegatingApplicationListener,\\\n	org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\\\n	org.springframework.boot.logging.ClasspathLoggingApplicationListener,\\\n	org.springframework.boot.logging.LoggingApplicationListener\n也就是说，在我们的例子中，listener最终会被初始化为ParentContextCloserApplicationListener，FileEncodingApplicationListener，AnsiOutputApplicationListener，ConfigFileApplicationListener，DelegatingApplicationListener，LiquibaseServiceLocatorApplicationListener，ClasspathLoggingApplicationListener，LoggingApplicationListener这几个类的对象组成的list。\n\n下图画出了加载的ApplicationListener，并说明了他们的作用。至于他们何时会被触发，等事件出现时，我们再说明。\n[![](http://zhaox.github.io/assets/images/SpringBootApplicationListener.png)](http://zhaox.github.io/assets/images/SpringBootApplicationListener.png)\n\n最后是mainApplicationClass\n\n	以下代码摘自：org.springframework.boot.SpringApplication\n\n	private Class<?> mainApplicationClass;\n\n	private void initialize(Object[] sources) {\n		...\n		// 为成员变量mainApplicationClass赋值\n		this.mainApplicationClass = deduceMainApplicationClass();\n		...\n	}\n\n	private Class<?> deduceMainApplicationClass() {\n		try {\n			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();\n			for (StackTraceElement stackTraceElement : stackTrace) {\n				if (\"main\".equals(stackTraceElement.getMethodName())) {\n					return Class.forName(stackTraceElement.getClassName());\n				}\n			}\n		}\n		catch (ClassNotFoundException ex) {\n			// Swallow and continue\n		}\n		return null;\n	}\n在deduceMainApplicationClass方法中，通过获取当前调用栈，找到入口方法main所在的类，并将其复制给SpringApplication对象的成员变量mainApplicationClass。在我们的例子中mainApplicationClass即是我们自己编写的Application类。\n\nSpringApplication对象的run方法\n经过上面的初始化过程，我们已经有了一个SpringApplication对象，根据SpringApplication类的静态run方法一节中的分析，接下来会调用SpringApplication对象的run方法。我们接下来就分析这个对象的run方法。\n\n\n	以下代码摘自：org.springframework.boot.SpringApplication\n\n	public ConfigurableApplicationContext run(String... args) {\n		StopWatch stopWatch = new StopWatch();\n		stopWatch.start();\n		ConfigurableApplicationContext context = null;\n		configureHeadlessProperty();\n		SpringApplicationRunListeners listeners = getRunListeners(args);\n		listeners.started();\n		try {\n			ApplicationArguments applicationArguments = new DefaultApplicationArguments(\n					args);\n			context = createAndRefreshContext(listeners, applicationArguments);\n			afterRefresh(context, applicationArguments);\n			listeners.finished(context, null);\n			stopWatch.stop();\n			if (this.logStartupInfo) {\n				new StartupInfoLogger(this.mainApplicationClass)\n						.logStarted(getApplicationLog(), stopWatch);\n			}\n			return context;\n		}\n		catch (Throwable ex) {\n			handleRunFailure(context, listeners, ex);\n			throw new IllegalStateException(ex);\n		}\n	}\n可变个数参数args即是我们整个应用程序的入口main方法的参数，在我们的例子中，参数个数为零。\n\nStopWatch是来自org.springframework.util的工具类，可以用来方便的记录程序的运行时间。\n\nSpringApplication对象的run方法创建并刷新ApplicationContext，算是开始进入正题了。下面按照执行顺序，介绍该方法所做的工作。\n\nheadless模式\n\n	以下代码摘自：org.springframework.boot.SpringApplication\n\n	private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = \"java.awt.headless\";\n	private boolean headless = true;\n\n	public ConfigurableApplicationContext run(String... args) {\n		...\n		//设置headless模式\n			configureHeadlessProperty();\n		...\n	}\n\n	private void configureHeadlessProperty() {\n		System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(\n				SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));\n	}\n实际上是就是设置系统属性java.awt.headless，在我们的例子中该属性会被设置为true，因为我们开发的是服务器程序，一般运行在没有显示器和键盘的环境。关于java中的headless模式，更多信息可以参考这里。\n\nSpringApplicationRunListeners\n\n	以下代码摘自：org.springframework.boot.SpringApplication\n\n	public ConfigurableApplicationContext run(String... args) {\n		...\n		SpringApplicationRunListeners listeners = getRunListeners(args);\n		listeners.started();\n		/**\n			 * 创建并刷新ApplicationContext\n			 * context = createAndRefreshContext(listeners, applicationArguments); \n			**/\n		listeners.finished(context, null);\n		...\n	}\n\n	private SpringApplicationRunListeners getRunListeners(String[] args) {\n		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };\n		return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(\n				SpringApplicationRunListener.class, types, this, args));\n	}\nrun方法中，加载了一系列SpringApplicationRunListener对象，在创建和更新ApplicationContext方法前后分别调用了listeners对象的started方法和finished方法, 并在创建和刷新ApplicationContext时，将listeners作为参数传递到了createAndRefreshContext方法中，以便在创建和刷新ApplicationContext的不同阶段，调用listeners的相应方法以执行操作。所以，所谓的SpringApplicationRunListeners实际上就是在SpringApplication对象的run方法执行的不同阶段，去执行一些操作，并且这些操作是可配置的。\n\n同时，可以看到，加载SpringApplicationRunListener时，使用的是跟加载ApplicationContextInitializer和ApplicationListener时一样的方法。那么加载了什么，就可以从spring.factories文件中看到了：\n\n\n	以下内容摘自spring-boot-1.3.3.RELEASE.jar中的资源文件META-INF/spring.factories\n\n	# Run Listeners\n	org.springframework.boot.SpringApplicationRunListener=\\\n	org.springframework.boot.context.event.EventPublishingRunListener\n可以看到，在我们的例子中加载的是org.springframework.boot.context.event.EventPublishingRunListener。我们看一看这个SpringApplicationRunListener究竟做了点什么工作了？\n\n	以下代码摘自：org.springframework.boot.context.event.EventPublishingRunListener\n\n	public EventPublishingRunListener(SpringApplication application, String[] args) {\n		this.application = application;\n		this.args = args;\n		this.multicaster = new SimpleApplicationEventMulticaster();\n		for (ApplicationListener<?> listener : application.getListeners()) {\n			this.multicaster.addApplicationListener(listener);\n		}\n	}\n\n	@Override\n	public void started() {\n		publishEvent(new ApplicationStartedEvent(this.application, this.args));\n	}\n\n	@Override\n	public void environmentPrepared(ConfigurableEnvironment environment) {\n		publishEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args,\n				environment));\n	}\n\n	@Override\n	public void contextPrepared(ConfigurableApplicationContext context) {\n		registerApplicationEventMulticaster(context);\n	}\n\n	@Override\n	public void contextLoaded(ConfigurableApplicationContext context) {\n		for (ApplicationListener<?> listener : this.application.getListeners()) {\n			if (listener instanceof ApplicationContextAware) {\n				((ApplicationContextAware) listener).setApplicationContext(context);\n			}\n			context.addApplicationListener(listener);\n		}\n		publishEvent(new ApplicationPreparedEvent(this.application, this.args, context));\n	}\n\n	@Override\n	public void finished(ConfigurableApplicationContext context, Throwable exception) {\n		publishEvent(getFinishedEvent(context, exception));\n	}\nEventPublishingRunListener在对象初始化时，将SpringApplication对象的成员变量listeners全都保存下来，然后在自己的public方法被调用时，发布相应的事件，或执行相应的操作。可以说这个RunListener是在SpringApplication对象的run方法执行到不同的阶段时，发布相应的event给SpringApplication对象的成员变量listeners中记录的事件监听器。\n\n下图画出了SpringApplicationRunListeners相关的类结构，虽然我们的例子中只有一个SpringApplicationRunListener，但在这样的设计下，想要扩展是非常容易的！\n[![](http://zhaox.github.io/assets/images/SpringBootSpringApplicationRunListener.png)](http://zhaox.github.io/assets/images/SpringBootSpringApplicationRunListener.png)\n\n接下来，我们看一下在调用listeners的started方法。在我们的例子中，也就是发布了ApplicationStartedEvent时，我们已经加载的事件监听器都做了什么操作。至于其它事件的发布，我们按照代码执行的顺序在后面的章节在介绍。\n\nParentContextCloserApplicationListener不监听ApplicationStartedEvent，没有操作；\nFileEncodingApplicationListener不监听ApplicationStartedEvent，没有操作；\nAnsiOutputApplicationListener不监听ApplicationStartedEvent，没有操作；\nConfigFileApplicationListener不监听ApplicationStartedEvent，没有操作；\nDelegatingApplicationListener不监听ApplicationStartedEvent，没有操作；\nLiquibaseServiceLocatorApplicationListener监听ApplicationStartedEvent，会检查classpath中是否有liquibase.servicelocator.ServiceLocator并做相应操作；\n\n	以下代码摘自：org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener\n\n	@Override\n	public void onApplicationEvent(ApplicationStartedEvent event) {\n		if (ClassUtils.isPresent(\"liquibase.servicelocator.ServiceLocator\", null)) {\n			new LiquibasePresent().replaceServiceLocator();\n		}\n	}\n我们的例子中，classpath中不存在liquibase，所以不执行任何操作。\n\nClasspathLoggingApplicationListener监听ApplicationStartedEvent，会打印classpath到debug日志；\n\n	@Override\n	public void onApplicationEvent(ApplicationEvent event) {\n		if (event instanceof ApplicationStartedEvent) {\n			if (this.logger.isDebugEnabled()) {\n				this.logger.debug(\"Application started with classpath: \" + getClasspath());\n		}\n		...\n	}\n\n	private String getClasspath() {\n		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();\n		if (classLoader instanceof URLClassLoader) {\n			return Arrays.toString(((URLClassLoader) classLoader).getURLs());\n		}\n		return \"unknown\";\n	}\n因为是debug级别的日志，而SpringBoot的默认日志级别是info级，所以我们在控制台不会看到classpath的输出。\n\nLoggingApplicationListener监听ApplicationStartedEvent，会根据classpath中的类情况创建相应的日志系统对象，并执行一些初始化之前的操作；\n\n	@Override\n	public void onApplicationEvent(ApplicationEvent event) {\n		if (event instanceof ApplicationStartedEvent) {\n			onApplicationStartedEvent((ApplicationStartedEvent) event);\n		}\n		...\n	}\n\n	private void onApplicationStartedEvent(ApplicationStartedEvent event) {\n		this.loggingSystem = LoggingSystem\n				.get(event.getSpringApplication().getClassLoader());\n		this.loggingSystem.beforeInitialize();\n	}\n我们的例子中，创建的是org.springframework.boot.logging.logback.LogbackLoggingSystem类的对象，Logback是SpringBoot默认采用的日志系统。下图画出了SpringBoot中的日志系统体系：\n[![](http://zhaox.github.io/assets/images/SpringBootLoggingSystem.png)](http://zhaox.github.io/assets/images/SpringBootLoggingSystem.png)\n\n好了，ApplicationStartedEvent事件的处理这样就结束了。以后在介绍事件处理的时候，我们只介绍监听该事件的监听器的操作，而不监听的，就不再说明了。\n\n创建并刷新ApplicationContext\n\n	以下代码摘自：org.springframework.boot.SpringApplication\n\n	public ConfigurableApplicationContext run(String... args) {\n		...\n		try {\n			ApplicationArguments applicationArguments = new DefaultApplicationArguments(\n					args);\n			context = createAndRefreshContext(listeners, applicationArguments);\n			afterRefresh(context, applicationArguments);\n			...\n		}\n		catch (Throwable ex) {\n			handleRunFailure(context, listeners, ex);\n			throw new IllegalStateException(ex);\n		}\n	}\n首先是创建一个DefaultApplicationArguments对象，之后调用createAndRefreshContext方法创建并刷新一个ApplicationContext，最后调用afterRefresh方法在刷新之后做一些操作。\n\n先来看看DefaultApplicationArguments吧：\n\n	以下代码摘自：org.springframework.boot.DefaultApplicationArguments\n\n	DefaultApplicationArguments(String[] args) {\n		Assert.notNull(args, \"Args must not be null\");\n		this.source = new Source(args);\n		this.args = args;\n	}\n\n	private static class Source extends SimpleCommandLinePropertySource {\n\n		Source(String[] args) {\n			super(args);\n		}\n		...\n	}\n\n	以下代码摘自：org.springframework.core.env.SimpleCommandLinePropertySource\n\n	public SimpleCommandLinePropertySource(String... args) {\n		super(new SimpleCommandLineArgsParser().parse(args));\n	}\n可以看到是把main函数的args参数当做一个PropertySource来解析。我们的例子中，args的长度为0，所以这里创建的DefaultApplicationArguments也没有实际的内容。\n\n创建并配置ApplicationConext的Environment\n\n	以下代码摘自：org.springframework.boot.SpringApplication\n\n	private ConfigurableEnvironment environment;\n\n	private boolean webEnvironment;\n\n	private ConfigurableApplicationContext createAndRefreshContext(\n			SpringApplicationRunListeners listeners,\n			ApplicationArguments applicationArguments) {\n		ConfigurableApplicationContext context;\n\n		// 创建并配置Environment\n		ConfigurableEnvironment environment = getOrCreateEnvironment();\n		configureEnvironment(environment, applicationArguments.getSourceArgs());\n		listeners.environmentPrepared(environment);\n		if (isWebEnvironment(environment) && !this.webEnvironment) {\n			environment = convertToStandardEnvironment(environment);\n		}\n\n		...\n\n		return context;\n	}\n\n	private ConfigurableEnvironment getOrCreateEnvironment() {\n		if (this.environment != null) {\n			return this.environment;\n		}\n		if (this.webEnvironment) {\n			return new StandardServletEnvironment();\n		}\n		return new StandardEnvironment();\n	}\nSpring Application的Environment代表着程序运行的环境，主要包含了两种信息，一种是profiles，用来描述哪些bean definitions是可用的；一种是properties，用来描述系统的配置，其来源可能是配置文件、JVM属性文件、操作系统环境变量等等。\n\n首先要调用getOrCreateEnvironment方法获取一个Environment对象。在我们的例子中，执行到此处时，environment成员变量为null，而webEnvironment成员变量的值为true，所以会创建一个StandardServletEnvironment对象并返回。\n\n之后是调用configureEnvironment方法来配置上一步获取的Environment对象，代码如下：\n\n\n	以下代码摘自：org.springframework.boot.SpringApplication\n\n	private Map<String, Object> defaultProperties;\n\n	private boolean addCommandLineProperties = true;\n\n	private Set<String> additionalProfiles = new HashSet<String>();\n\n	protected void configureEnvironment(ConfigurableEnvironment environment,\n			String[] args) {\n		configurePropertySources(environment, args);\n		configureProfiles(environment, args);\n	}\n\n	protected void configurePropertySources(ConfigurableEnvironment environment,\n			String[] args) {\n		MutablePropertySources sources = environment.getPropertySources();\n		if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {\n			sources.addLast(\n					new MapPropertySource(\"defaultProperties\", this.defaultProperties));\n		}\n		if (this.addCommandLineProperties && args.length > 0) {\n			String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;\n			if (sources.contains(name)) {\n				PropertySource<?> source = sources.get(name);\n				CompositePropertySource composite = new CompositePropertySource(name);\n				composite.addPropertySource(new SimpleCommandLinePropertySource(\n						name + \"-\" + args.hashCode(), args));\n				composite.addPropertySource(source);\n				sources.replace(name, composite);\n			}\n			else {\n				sources.addFirst(new SimpleCommandLinePropertySource(args));\n			}\n		}\n	}\n\n	protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {\n		environment.getActiveProfiles(); // ensure they are initialized\n		// But these ones should go first (last wins in a property key clash)\n		Set<String> profiles = new LinkedHashSet<String>(this.additionalProfiles);\n		profiles.addAll(Arrays.asList(environment.getActiveProfiles()));\n		environment.setActiveProfiles(profiles.toArray(new String[profiles.size()]));\n	}\nconfigureEnvironment方法先是调用configurePropertySources来配置properties，然后调用configureProfiles来配置profiles。\n\nconfigurePropertySources首先查看SpringApplication对象的成员变量defaultProperties，如果该变量非null且内容非空，则将其加入到Environment的PropertySource列表的最后。然后查看SpringApplication对象的成员变量addCommandLineProperties和main函数的参数args，如果设置了addCommandLineProperties=true，且args个数大于0，那么就构造一个由main函数的参数组成的PropertySource放到Environment的PropertySource列表的最前面(这就能保证，我们通过main函数的参数来做的配置是最优先的，可以覆盖其他配置）。在我们的例子中，由于没有配置defaultProperties且main函数的参数args个数为0，所以这个函数什么也不做。\n\nconfigureProfiles首先会读取Properties中key为spring.profiles.active的配置项，配置到Environment，然后再将SpringApplication对象的成员变量additionalProfiles加入到Environment的active profiles配置中。在我们的例子中，配置文件里没有spring.profiles.active的配置项，而SpringApplication对象的成员变量additionalProfiles也是一个空的集合，所以这个函数没有配置任何active profile。\n\n到现在，Environment就算是配置完成了。接下来调用SpringApplicationRunListeners类的对象listeners发布ApplicationEnvironmentPreparedEvent事件：\n\n\n	以下代码摘自：org.springframework.boot.context.event.EventPublishingRunListener\n\n	@Override\n	public void environmentPrepared(ConfigurableEnvironment environment) {\n		publishEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args,\n				environment));\n	}\n好，现在来看一看我们加载的ApplicationListener对象都有哪些响应了这个事件，做了什么操作：\n\nFileEncodingApplicationListener响应该事件，检查file.encoding配置是否与spring.mandatory_file_encoding一致：\n\n	以下代码摘自：org.springframework.boot.context.FileEncodingApplicationListener\n\n	@Override\n	public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {\n		RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(\n				event.getEnvironment(), \"spring.\");\n		if (resolver.containsProperty(\"mandatoryFileEncoding\")) {\n			String encoding = System.getProperty(\"file.encoding\");\n			String desired = resolver.getProperty(\"mandatoryFileEncoding\");\n			if (encoding != null && !desired.equalsIgnoreCase(encoding)) {\n				logger.error(\"System property \'file.encoding\' is currently \'\" + encoding\n						+ \"\'. It should be \'\" + desired\n						+ \"\' (as defined in \'spring.mandatoryFileEncoding\').\");\n				logger.error(\"Environment variable LANG is \'\" + System.getenv(\"LANG\")\n						+ \"\'. You could use a locale setting that matches encoding=\'\"\n						+ desired + \"\'.\");\n				logger.error(\"Environment variable LC_ALL is \'\" + System.getenv(\"LC_ALL\")\n						+ \"\'. You could use a locale setting that matches encoding=\'\"\n						+ desired + \"\'.\");\n				throw new IllegalStateException(\n						\"The Java Virtual Machine has not been configured to use the \"\n								+ \"desired default character encoding (\" + desired\n								+ \").\");\n			}\n		}\n	}\n在我们的例子中，因为没有spring.mandatory_file_encoding的配置，所以这个响应方法什么都不做。\n\nAnsiOutputApplicationListener响应该事件，根据spring.output.ansi.enabled和spring.output.ansi.console-available对AnsiOutput类做相应配置:\n\n	以下代码摘自：org.springframework.boot.context.config.AnsiOutputApplicationListener\n\n	@Override\n	public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {\n		RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(\n				event.getEnvironment(), \"spring.output.ansi.\");\n		if (resolver.containsProperty(\"enabled\")) {\n			String enabled = resolver.getProperty(\"enabled\");\n			AnsiOutput.setEnabled(Enum.valueOf(Enabled.class, enabled.toUpperCase()));\n		}\n\n		if (resolver.containsProperty(\"console-available\")) {\n			AnsiOutput.setConsoleAvailable(\n					resolver.getProperty(\"console-available\", Boolean.class));\n		}\n	}\n我们的例子中，这两项配置都是空的，所以这个响应方法什么都不做。\n\nConfigFileApplicationListener加载该事件，从一些约定的位置加载一些配置文件，而且这些位置是可配置的。\n\n	以下代码摘自：org.springframework.boot.context.config.ConfigFileApplicationListener\n\n	@Override\n	public void onApplicationEvent(ApplicationEvent event) {\n		if (event instanceof ApplicationEnvironmentPreparedEvent) {\n			onApplicationEnvironmentPreparedEvent(\n					(ApplicationEnvironmentPreparedEvent) event);\n		}\n		if (event instanceof ApplicationPreparedEvent) {\n			onApplicationPreparedEvent(event);\n		}\n	}\n\n	private void onApplicationEnvironmentPreparedEvent(\n			ApplicationEnvironmentPreparedEvent event) {\n		List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();\n		postProcessors.add(this);\n		AnnotationAwareOrderComparator.sort(postProcessors);\n		for (EnvironmentPostProcessor postProcessor : postProcessors) {\n			postProcessor.postProcessEnvironment(event.getEnvironment(),\n					event.getSpringApplication());\n		}\n	}\n\n	List<EnvironmentPostProcessor> loadPostProcessors() {\n		return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class,\n				getClass().getClassLoader());\n	}\n\n\n	以下内容摘自spring-boot-1.3.3.RELEASE.jar中的资源文件META-INF/spring.factories\n\n	# Environment Post Processors\n	org.springframework.boot.env.EnvironmentPostProcessor=\\\n	org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\\\n	org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor\n可以看到，ConfigFileApplicationListener从META-INF/spring.factories文件中读取EnvironmentPostProcessor配置，加载相应的EnvironmentPostProcessor类的对象，并调用其postProcessEnvironment方法。在我们的例子中，会加载CloudFoundryVcapEnvironmentPostProcessor和SpringApplicationJsonEnvironmentPostProcessor并执行，由于我们的例子中没有CloudFoundry和Json的配置，所以这个响应，不会加载任何的配置文件到Environment中来。\n\nDelegatingApplicationListener响应该事件，将配置文件中key为context.listener.classes的配置项，加载在成员变量multicaster中：\n\n	以下内容摘自：org.springframework.boot.context.config.DelegatingApplicationListener\n\n	private static final String PROPERTY_NAME = \"context.listener.classes\";\n\n	private SimpleApplicationEventMulticaster multicaster;\n\n	@Override\n	public void onApplicationEvent(ApplicationEvent event) {\n		if (event instanceof ApplicationEnvironmentPreparedEvent) {\n			List<ApplicationListener<ApplicationEvent>> delegates = getListeners(\n					((ApplicationEnvironmentPreparedEvent) event).getEnvironment());\n			if (delegates.isEmpty()) {\n				return;\n			}\n			this.multicaster = new SimpleApplicationEventMulticaster();\n			for (ApplicationListener<ApplicationEvent> listener : delegates) {\n				this.multicaster.addApplicationListener(listener);\n			}\n		}\n		if (this.multicaster != null) {\n			this.multicaster.multicastEvent(event);\n		}\n	}\n\n	@SuppressWarnings(\"unchecked\")\n	private List<ApplicationListener<ApplicationEvent>> getListeners(\n			ConfigurableEnvironment env) {\n		String classNames = env.getProperty(PROPERTY_NAME);\n		List<ApplicationListener<ApplicationEvent>> listeners = new ArrayList<ApplicationListener<ApplicationEvent>>();\n		if (StringUtils.hasLength(classNames)) {\n			for (String className : StringUtils.commaDelimitedListToSet(classNames)) {\n				try {\n					Class<?> clazz = ClassUtils.forName(className,\n							ClassUtils.getDefaultClassLoader());\n					Assert.isAssignable(ApplicationListener.class, clazz, \"class [\"\n							+ className + \"] must implement ApplicationListener\");\n					listeners.add((ApplicationListener<ApplicationEvent>) BeanUtils\n							.instantiateClass(clazz));\n				}\n				catch (Exception ex) {\n					throw new ApplicationContextException(\n							\"Failed to load context listener class [\" + className + \"]\",\n							ex);\n				}\n			}\n		}\n		AnnotationAwareOrderComparator.sort(listeners);\n		return listeners;\n	}\n我们的例子中，因为没有key为context.listener.classes的Property，所以不会加载任何listener到该监听器中。\n\nLoggingApplicationListener响应该事件，并对在ApplicationStarted时加载的LoggingSystem做一些初始化工作：\n\n	以下代码摘自：org.springframework.boot.logging.LoggingApplicationListener\n\n	@Override\n	public void onApplicationEvent(ApplicationEvent event) {\n		if (event instanceof ApplicationStartedEvent) {\n			onApplicationStartedEvent((ApplicationStartedEvent) event);\n		}\n		else if (event instanceof ApplicationEnvironmentPreparedEvent) {\n			onApplicationEnvironmentPreparedEvent(\n					(ApplicationEnvironmentPreparedEvent) event);\n		}\n		else if (event instanceof ApplicationPreparedEvent) {\n			onApplicationPreparedEvent((ApplicationPreparedEvent) event);\n		}\n		else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event)\n				.getApplicationContext().getParent() == null) {\n			onContextClosedEvent();\n		}\n	}\n\n	private void onApplicationEnvironmentPreparedEvent(\n			ApplicationEnvironmentPreparedEvent event) {\n		if (this.loggingSystem == null) {\n			this.loggingSystem = LoggingSystem\n					.get(event.getSpringApplication().getClassLoader());\n		}\n		initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());\n	}\n\n	protected void initialize(ConfigurableEnvironment environment,\n			ClassLoader classLoader) {\n		LogFile logFile = LogFile.get(environment);\n		setSystemProperties(environment, logFile);\n		initializeEarlyLoggingLevel(environment);\n		initializeSystem(environment, this.loggingSystem, logFile);\n		initializeFinalLoggingLevels(environment, this.loggingSystem);\n		registerShutdownHookIfNecessary(environment, this.loggingSystem);\n	}\n在我们的例子中，是对加载的LogbackLoggingSystem做一些初始化工作。关于日志系统更详细的讨论，值得再写一篇文章，就不在这里展开讨论了。\n\n打印banner\n\n	以下代码摘自：org.springframework.boot.SpringApplication\n\n	private Banner banner;\n\n	private Banner.Mode bannerMode = Banner.Mode.CONSOLE;\n\n	public static final String BANNER_LOCATION_PROPERTY = \"banner.location\";\n\n	public static final String BANNER_LOCATION_PROPERTY_VALUE = \"banner.txt\";\n\n	private static final Banner DEFAULT_BANNER = new SpringBootBanner();\n\n\n	private ConfigurableApplicationContext createAndRefreshContext(\n			SpringApplicationRunListeners listeners,\n			ApplicationArguments applicationArguments) {\n\n		...\n		if (this.bannerMode != Banner.Mode.OFF) {\n			printBanner(environment);\n		}\n		...\n	}\n\n	protected void printBanner(Environment environment) {\n		Banner selectedBanner = selectBanner(environment);\n		if (this.bannerMode == Banner.Mode.LOG) {\n			try {\n				logger.info(createStringFromBanner(selectedBanner, environment));\n			}\n			catch (UnsupportedEncodingException ex) {\n				logger.warn(\"Failed to create String for banner\", ex);\n			}\n		}\n		else {\n			selectedBanner.printBanner(environment, this.mainApplicationClass,\n					System.out);\n		}\n	}\n\n	private Banner selectBanner(Environment environment) {\n		String location = environment.getProperty(BANNER_LOCATION_PROPERTY,\n				BANNER_LOCATION_PROPERTY_VALUE);\n		ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader\n				: new DefaultResourceLoader(getClassLoader());\n		Resource resource = resourceLoader.getResource(location);\n		if (resource.exists()) {\n			return new ResourceBanner(resource);\n		}\n		if (this.banner != null) {\n			return this.banner;\n		}\n		return DEFAULT_BANNER;\n	}\n\n	private String createStringFromBanner(Banner banner, Environment environment)\n			throws UnsupportedEncodingException {\n		ByteArrayOutputStream baos = new ByteArrayOutputStream();\n		banner.printBanner(environment, this.mainApplicationClass, new PrintStream(baos));\n		String charset = environment.getProperty(\"banner.charset\", \"UTF-8\");\n		return baos.toString(charset);\n	}\nprintBanner方法中，首先会调用selectBanner方法得到一个banner对象，然后判断bannerMode的类型，如果是Banner.Mode.LOG，那么将banner对象转换为字符串，打印一条info日志，否则的话，调用banner对象的printbanner方法，将banner打印到标准输出System.out。\n\n在我们的例子中，bannerMode是Banner.Mode.Console，而且也不曾提供过banner.txt这样的资源文件。所以selectBanner方法中得到到便是默认的banner对象，即SpringBootBanner类的对象：\n\n\n	以下代码摘自：org.springframework.boot.SpringBootBanner\n\n	private static final String[] BANNER = { \"\",\n			\"  .   ____          _            __ _ _\",\n			\" /\\\\\\\\ / ___\'_ __ _ _(_)_ __  __ _ \\\\ \\\\ \\\\ \\\\\",\n			\"( ( )\\\\___ | \'_ | \'_| | \'_ \\\\/ _` | \\\\ \\\\ \\\\ \\\\\",\n			\" \\\\\\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )\",\n			\"  \'  |____| .__|_| |_|_| |_\\\\__, | / / / /\",\n			\" =========|_|==============|___/=/_/_/_/\" };\n\n	private static final String SPRING_BOOT = \" :: Spring Boot :: \";\n\n	private static final int STRAP_LINE_SIZE = 42;\n\n	@Override\n	public void printBanner(Environment environment, Class<?> sourceClass,\n			PrintStream printStream) {\n		for (String line : BANNER) {\n			printStream.println(line);\n		}\n		String version = SpringBootVersion.getVersion();\n		version = (version == null ? \"\" : \" (v\" + version + \")\");\n		String padding = \"\";\n		while (padding.length() < STRAP_LINE_SIZE\n				- (version.length() + SPRING_BOOT.length())) {\n			padding += \" \";\n		}\n\n		printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT,\n				AnsiColor.DEFAULT, padding, AnsiStyle.FAINT, version));\n		printStream.println();\n	}\n先打印个Spring的图形，然后打印个Spring Boot的文本，再然后打印一下Spring Boot的版本。会在控制台看到如下输出：\n\n\n	以下内容是程序启动后在console的输出：\n\n	  .   ____          _            __ _ _\n	 /\\\\ / ___\'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\\n	( ( )\\___ | \'_ | \'_| | \'_ \\/ _` | \\ \\ \\ \\\n	 \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )\n	  \'  |____| .__|_| |_|_| |_\\__, | / / / /\n	 =========|_|==============|___/=/_/_/_/\n	 :: Spring Boot ::        (v1.3.3.RELEASE)\n	我的天。分析启动流程这么久，终于在屏幕有一行输出了，不容易。\n\n	创建ApplicationContext\n\n	private Class<? extends ConfigurableApplicationContext> applicationContextClass;\n\n	public static final String DEFAULT_CONTEXT_CLASS = \"org.springframework.context.\"\n			+ \"annotation.AnnotationConfigApplicationContext\";\n\n	public static final String DEFAULT_WEB_CONTEXT_CLASS = \"org.springframework.\"\n			+ \"boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext\";\n\n	private ConfigurableApplicationContext createAndRefreshContext(\n			SpringApplicationRunListeners listeners,\n			ApplicationArguments applicationArguments) {\n		ConfigurableApplicationContext context;\n\n		...\n\n		context = createApplicationContext();\n		context.setEnvironment(environment);\n		postProcessApplicationContext(context);\n		applyInitializers(context);\n		listeners.contextPrepared(context);\n		if (this.logStartupInfo) {\n			logStartupInfo(context.getParent() == null);\n			logStartupProfileInfo(context);\n		}\n\n		...\n\n		return context;\n	}\n\n\n	protected ConfigurableApplicationContext createApplicationContext() {\n		Class<?> contextClass = this.applicationContextClass;\n		if (contextClass == null) {\n			try {\n				contextClass = Class.forName(this.webEnvironment\n						? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);\n			}\n			catch (ClassNotFoundException ex) {\n				throw new IllegalStateException(\n						\"Unable create a default ApplicationContext, \"\n								+ \"please specify an ApplicationContextClass\",\n						ex);\n			}\n		}\n		return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);\n	}\ncreateAndRefreshContext中调用createApplicationContext获取创建ApplicationContext，可以看到，当检测到本次程序是一个web应用程序（成员变量webEnvironment为true）的时候，就加载类DEFAULT_WEB_CONTEXT_CLASS，否则的话加载DEFAULT_CONTEXT_CLASS。我们的例子是一个web应用程序，所以会加载DEFAULT_WEB_CONTEXT_CLASS，也就是org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext。我们先来看一看这个AnnotationConfigEmbeddedWebApplicationContext具体有什么功能。下图画出了它的继承体系。\n\n[![](http://zhaox.github.io/assets/images/SpringBootApplicationContext.png)](http://zhaox.github.io/assets/images/SpringBootApplicationContext.png)\n\n可以看到我们加载的这个AnnotationConfigEmbeddedWebApplicationContext类，从名字就可以看出来，首先是一个WebApplicationContext实现了WebApplicationContext接口，然后是一个EmbeddedWebApplicationContext，这意味着它会自动创建并初始化一个EmbeddedServletContainer，同时还支持AnnotationConfig，会将使用注解标注的bean注册到ApplicationContext中。更详细的过程，后面在例子中再一一剖析。\n\n可以看到在加载类对象AnnotationConfigEmbeddedWebApplicationContext之后，createApplicationContext方法中紧接着调用BeanUtils的instantiate方法来创建ApplicationContext对象，其代码如下：\n\n\n	以下代码摘自：org.springframework.beans.BeanUtils\n\n	public static <T> T instantiate(Class<T> clazz) throws BeanInstantiationException {\n		Assert.notNull(clazz, \"Class must not be null\");\n		if (clazz.isInterface()) {\n			throw new BeanInstantiationException(clazz, \"Specified class is an interface\");\n		}\n		try {\n			return clazz.newInstance();\n		}\n		catch (InstantiationException ex) {\n			throw new BeanInstantiationException(clazz, \"Is it an abstract class?\", ex);\n		}\n		catch (IllegalAccessException ex) {\n			throw new BeanInstantiationException(clazz, \"Is the constructor accessible?\", ex);\n		}\n	}\n通过调用Class对象的newInstance()方法来实例化对象，这等同于直接调用类的空的构造方法，所以我们来看AnnotationConfigEmbeddedWebApplicationContext类的构造方法：\n\n\n	以下代码摘自：org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext\n\n	public AnnotationConfigEmbeddedWebApplicationContext() {\n		this.reader = new AnnotatedBeanDefinitionReader(this);\n		this.scanner = new ClassPathBeanDefinitionScanner(this);\n	}\n\n	@Override\n	public void setEnvironment(ConfigurableEnvironment environment) {\n		super.setEnvironment(environment);\n		this.reader.setEnvironment(environment);\n		this.scanner.setEnvironment(environment);\n	}\n构造方法中初始化了两个成员变量，类型分别为AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner用以加载使用注解的bean定义。\n\n这样ApplicationContext对象就创建出来了，在createAndRefreshContext方法中创建了ApplicationContext对象之后会紧接着调用其setEnvironment将我们之前准备好的Environment对象赋值进去。之后分别调用postProcessApplicationContext和applyInitializers做一些处理和初始化的操作。\n\n先来看看postProcessApplicationContext：\n\n\n	protected void postProcessApplicationContext(ConfigurableApplicationContext context) {\n		if (this.webEnvironment) {\n			if (context instanceof ConfigurableWebApplicationContext) {\n				ConfigurableWebApplicationContext configurableContext = (ConfigurableWebApplicationContext) context;\n				if (this.beanNameGenerator != null) {\n					configurableContext.getBeanFactory().registerSingleton(\n							AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,\n							this.beanNameGenerator);\n				}\n			}\n		}\n		if (this.resourceLoader != null) {\n			if (context instanceof GenericApplicationContext) {\n				((GenericApplicationContext) context)\n						.setResourceLoader(this.resourceLoader);\n			}\n			if (context instanceof DefaultResourceLoader) {\n				((DefaultResourceLoader) context)\n						.setClassLoader(this.resourceLoader.getClassLoader());\n			}\n		}\n	}\n如果成员变量beanNameGenerator不为Null，那么为ApplicationContext对象注册beanNameGenerator bean。如果成员变量resourceLoader不为null，则为ApplicationContext对象设置ResourceLoader。我们的例子中，这两个成员变量都为Null，所以什么都不做。\n\n之后是applyInitializers方法：\n\n\n	protected void applyInitializers(ConfigurableApplicationContext context) {\n		for (ApplicationContextInitializer initializer : getInitializers()) {\n			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(\n					initializer.getClass(), ApplicationContextInitializer.class);\n			Assert.isInstanceOf(requiredType, context, \"Unable to call initializer.\");\n			initializer.initialize(context);\n		}\n	}\n\n	public Set<ApplicationContextInitializer<?>> getInitializers() {\n		return asUnmodifiableOrderedSet(this.initializers);\n	}\n\n	private static <E> Set<E> asUnmodifiableOrderedSet(Collection<E> elements) {\n		List<E> list = new ArrayList<E>();\n		list.addAll(elements);\n		Collections.sort(list, AnnotationAwareOrderComparator.INSTANCE);\n		return new LinkedHashSet<E>(list);\n	}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n', 'Spring Boot启动流程详解', '1', 'Coriger', '2019-01-16 18:13:11', null, '53'), ('254874', '10001', '分布式ID生成器解决方案', '`一，登录`\n		1.什么情况下我们需要ID生成器\n				数据库水平拆分的情况下，主键由于需要作为业务标识使用，需要唯一。\n				业务编号需要暴露给用户，但是又不想被用户猜到需要被隐藏的业务编号\n				业务编号需要体现业务信息，比如订单分类订单渠道等等\n		2. ID生成器设计目标\n				全局唯一\n				每秒可生成100W +\n				趋于递增（对索引友好）\n				高可用\n				可伸缩\n\n`二，常见ID生成方案`\n	1. UUID\n			UUID是通用唯一识别码（通用唯一标识符）的缩写，是一种软件建构的标准，亦为开放软件基金会组织在分布式计算环境领域的一部分\n			UUID是由一组32位数的16进制数字所构成，是故UUID理论上的总数为1632 = 2128，约等于3.4 x 1038.也就是说若每纳秒产生1兆个UUID，要花100亿年才会将所有UUID用完.\n			UUID的标准型式包含32个16进制数字，以连字号分为五段，形式为8-4-4-4-12的32个字符。示例：550e8400-e29b-41d4-a716-446655440000 \n**每秒产生10亿笔UUID，100年后只产生一次重复的机率是50％**\n\n	优点：\n			本地生成，没有网络消耗\n			可以任意水平扩展\n			生成效率高\n			生成节点不限\n	缺点：\n			没有排序，无法保证趋势递增。\n			UUID往往是使用字符串存储，查询的效率比较低。\n			存储空间比较大，如果是海量数据库，就需要考虑存储量的问题。\n			传输数据量大\n			不可读\n\n	2.数据库自增列\n			可以通过设置BIGINT类型的数据库自增列，在事务中通过插入操作获取主键标识\n\n	优点：\n			可以实现ID完全递增\n			部署简单，有DB就可以\n	缺点：\n			生成效率差，取决于数据库性能指标，每秒生成一万ID都很难\n			依赖于数据库，如果数据库发生故障，在做主从切换的时候可能会引发BUG\n\n	3. Redis生成ID\n			当使用数据库来生成ID性能不够要求的时候，我们可以尝试使用Redis来生成ID。这主要依赖于Redis是单线程的，所以也可以用生成全局唯一的ID。可以用Redis的原子操作INCR和INCRBY来实现。\n			可以使用的Redis集群来获取更高的吞吐量假如。一个集群中有5个Redis的。可以初始化每台Redis的的值分别是1,2,3,4,5，然后步长都是5。各个Redis生成的ID为：\n					A：1,6,11,16,21\n					B：2,7,12,17,22\n					C：3,8,13,18,23\n					D：4,9,14,19,24\n					E：5,10,15,20,25\n			这个，随便负载到哪个机确定好，未来很难做修改。但是3-5台服务器基本能够满足器上，都可以获得不同的ID。但是步长和初始值一定需要事先需要了。使用的Redis集群也可以方式单点故障的问题。\n			另外，比较适合使用的Redis来生成每天从0开始的流水号。比如订单号=日期+当日自增长号。可以每天在Redis的中生成一个密钥，使用INCR进行累加。\n\n	优点：\n			不依赖于数据库，灵活方便，且性能优于数据库。\n			数字ID天然排序，对分页或者需要排序的结果很有帮助。\n	缺点：\n			如果系统中没有Redis的，还需要引入新的组件，增加系统复杂度。\n			需要编码和配置的工作量比较大。\n\n	4. MongoDB的ObjectId\n			MongoDB的ObjectId和雪花算法类似。它设计成轻量型的，不同的机器都能用全局唯一的同种方法方便地生成它.MongoDB从一开始就设计用来作为分布式数据库，处理多个节点是一个核心要求。使其在分片环境中要容易生成得多。\n	其格式如下：\n[![](https://box.kancloud.cn/1e4a893f81e6ea1fb879b887d9d8a4c7_757x253.png)](https://box.kancloud.cn/1e4a893f81e6ea1fb879b887d9d8a4c7_757x253.png)\n			前4个字节是从标准纪元开始的时间戳，单位为秒。时间戳，与随后的5个字节组合起来，提供了秒级别的唯一性。由于时间戳在前，这意味着ObjectId大致会按照插入的顺序排列。这对于某些方面很有用，如将其作为索引提高效率。这4个字节也隐含了文档创建的时间。绝大多数客户端类库都会公开一个方法从ObjectId获取这个信息。\n			接下来的3字节是所在主机的唯一标识符。通常是机器主机名的散列值。这样就可以确保不同主机生成不同的的ObjectId，不产生冲突。为了确保在同一台机器上并发的多个进程产生的的ObjectId是 一的，接下来的两字节来自产生的ObjectId的进程标识符（PID）。\n			前9字节保证了同一秒钟不同机器不同进程产生的的ObjectId是唯一的。后3字节就是一个自动增加的计数器，确保相同进程同一秒产生的ObjectId也是不一样的。同一秒钟最多允许每个进程拥有2563（16 777 216）个不同的ObjectId。\n\n	5. Twitter的雪花算法\n			雪花是Twitter的开源的分布式ID生成算法，结果是一个长型的ID其核心思想是：使用41bit作为毫秒数，10位作为机器的ID（5个比特是数据中心，5个位的机器ID） ，12bit作为毫秒内的流水号（意味着每个节点在每毫秒可以产生4096个ID），最后还有一个符号位，永远是0。\n[![](https://box.kancloud.cn/4f49804e02d591599ae3aac8f1f10e32_1021x346.png)](https://box.kancloud.cn/4f49804e02d591599ae3aac8f1f10e32_1021x346.png)\n\n	分段					作用													说明\n	1位					  保留													-\n	41bit					时间戳，精确到毫秒							可以支持69年的跨度\n	5位					   DatacenterId									  可以最多支持32个节点\n	5位					   WorkerId											可以最多支持32个节点\n	12位					  毫秒内的计数									 支持每个节点每毫秒产生4096个ID\n\n	优点：\n			ID趋势递增\n			生成效率高，单机每秒400W +\n			支持线性扩充\n			稳定性高，不依赖DB等服务\n	缺点：\n			依赖服务器时间，如果服务器时间发生回拨，可能导致生成重复ID\n			在单机上是递增的，但是由于涉及到分布式环境，每台机器上的时钟不可能完全同步，也许有时候也会出现不是全局递增的情况', '分布式ID生成器解决方案', '1', 'Coriger', '2019-04-15 22:35:47', null, '0'), ('255544', '9999', 'JB.Gingerbread-Blog使用篇', '## 前言\n\n前面一节简单的介绍了[blog的来源和页面展示]。本节主要讲述一下如果运行和使用JB.Gingerbread-blog以及自己开发过程。\n\n2017年5月20日更新\n\n## 博客使用\n\n### 运行步骤\n\n1.下载[源码]\n2.修改数据库连接信息；\n3.将JB.Gingerbread-blog的sql文件导入到mysql数据库中；\n4.直接使用maven工具 mvn package -DskipTests进行打包；\n5.使用命令 **java -jar JB.Gingerbread-blog.jar** 运行项目\n6.访问 **http://localhost:8080** 进入主页\n\n### 后台模块\n\n **http://localhost:8080/login** 进行登陆\n\n默认账号密码   admin/******\n\n## 技术难度评估\n\n​	对技术栈要求不高，spring及spring boot框架技术就能搞定，thymeleaf模板语言的语法简单易学，spring-boot也是可以很快入门。\n\n##  开发过程\n之前搞业务后端开发比较久，太多东西生疏了，一直都在改。后端难度较低，花费的时间较少，也存在很多`烂代码`,后续会进行重构的。\n\n## 结语\n\n如果想部署到服务器请参看[服务器环境搭建篇]\n\n总结一点：实践出真知。\n\n\n\n\n\n\n\n\n\n\n', '本篇主要介绍如何运行本项目，其实和普通项目没什么区别，只是打包成了可执行得jar包。非常符合spring boot的特性。', '0', 'JB.Gingerbread', '2017-05-18 20:03:01', '2017-07-10 17:31:08', '1278'), ('255784', '10001', 'Tomcat的结构与启动顺序', '[![Tomcat的结构](http://p1.pstatp.com/large/pgc-image/15255757305346fa772ddbb \"Tomcat的结构\")](http://p1.pstatp.com/large/pgc-image/15255757305346fa772ddbb \"Tomcat的结构\")\n\n服务器（服务器）\n\n服务器代表整个Tomcat容器。Tomcat提供了服务器接口的默认实现（很少由用户定制）。\n\n服务（服务）\n\n服务是位于服务器内部的中间组件，将一个或多个连接器（连接器）绑定到一个引擎（发动机）。默认实现很简单且足够，很少由用户自定义。\n\n连接器（连接器）\n\n连接器处理与客户端的通信。Tomcat有多个可用的连接器，例如HTTP连接器，AJP连接器。\n\nHTTP连接器：负责与客户端建立HTTP连接。\n\nAJP连接器：负责与其他网络服务器建立连接。\n\nPS：AJP是定向包协议。因为性能原因，使用二进制格式来传输可读性文本。\n\n集装箱（容器）\n\n容器容器由四个子容器组件构成，分别是：发动机，主机，语境，集群。\n\n发动机（引擎）\n\n引擎是特定服务的请求处理管道。服务可能有多个连接器，引擎会接收并处理这些连接器的所有请求，并将响应传递给适当的连接器。引擎接口可以定制实现，但并不常见。\n\nPS：引擎可以通过设置的jvmRoute参数用于Tomcat的服务器集群。\n\n主机（主机）\n\n主机是Tomcat的服务器里网络名称的关联。引擎可能包含多个主机，主机元素也支持网络别名。\n\n上下文（上下文）\n\n一个上下文代表一个网络应用程序。一个主机可能包含多个上下文，每个上下文都有唯一的路径。\n\n集群（集群）\n\nTomcat的集群实现提供会话复制，上下文属性复制和WAR文件的集群部署。\n\nserver.xml的例子\n\n<服务名称=“Catalina”> <Connector connectionTimeout =“20000”port =“8080”protocol =“HTTP / 1.1”redirectPort =“8443”/> <Connector port =“8009”protocol =“AJP / 1.3”redirectPort = “8443”/> <Engine defaultHost =“localhost”name =“Catalina”> <！ - <Cluster className =“org.apache.catalina.ha.tcp.SimpleTcpCluster”/> - > <Host appBase =“webapps “autoDeploy =”true“name =”localhost“unpackWARs =”true“> <Context docBase =”test“path =”/ test“reloadable =”true“source =”org.eclipse.jst.jee.server：test“ /> </主机> </发动机> </服务>\n服务器启动\n\n启动方式\n\n如图1所示，命令行启动。\n\n2，作为Java的程序的嵌入式服务器。\n\n3，窗口服务自动启动。\n\nTomcat的启动顺序\n\n从命令行启动\n\n类：org.apache.catalina.startup.Bootstrap\n\n如图1所示，设置类加载器\n\n2，加载启动类（反射）\n\n3，Bootstrap.daemon.init（）完成\n\n处理命令行参数（开始，停止）\n\n1，Catalina.setAwait（真）;\n\n2，Catalina.load（）;\n\ninitDirs - > initNaming - > createStartDigester - > server.xml加载 - > System.out和System.err分配给SystemLogHandler类 - >初始化所有组件\n\n3，Catalina.start（）;\n\n启动NamingContext - >启动服务 - > StandardHost\n\n如图4所示，通过HTTP端口接收请求\n\n5，调用的servlet类\n\n', 'Tomcat的结构与启动顺序', '1', 'Coriger', '2018-05-21 23:03:41', null, '296'), ('255836', '10001', 'JVM调优并解决OutOfMemoryError，StackOverf', 'JVM调优并解决OutOfMemoryError，StackOverflowError\n\nJVM 调优，首先应从内存开始，尤其是在真正的的web服务部署的时候。因为真正的web服务会比开发的时候花费更多的内存，用来处理多用户并发的情况。本人多次吃过这方面的亏，所以整理一下，希望能给别人以帮助。\n\n这个年头变啦，内存变得如大白菜，每个新装的机器都2G以上的内存，甚至4G，也不是什么新闻。而软件‘吃’内存的情况则变化不大（除了VIsta）。但 JAVA诞生的时候可不是这样——95年，想来当年97年，64M的内存还要500元，所以ＪＶＭ初始化对内存的要不能太大，而且也要考虑老机器的情况，毕竟现在JRE基本跑在每个人的机器上。但是ＪＶＭ初始占用还停留在几年前的情况下，确实没有跟上软件和硬件的发展。而像Tomcat, JBoss, Eclipse(尤其安上MyEclipse插件后），也考虑到每台机器的内存情况，所以初始话定义都很低，经常会抛内存溢出Bug。\n\n好，言归正传。我们先从解决bug开始，当Java程序申请内存，超出VM可分配内纯的时候，ＶＭ首先可能会ＧＣ，如果ＧＣ完还是不够，或者申请的直接超够ＶＭ可能有的，就会抛出内存溢出异常。从ＶＭ规范中我们可以得到，一下几种异常。\n\njava.lang.StackOverflowError:（很少）\n\njava.lang.OutOfMemoryError：heap space(比较常见)\n\njava.lang.OutOfMemoryError: PermGen space (经常出现)\n\n以下分别解释一下，从最常见的开始：\n\njava.lang.OutOfMemoryError: PermGen space 这个异常比较常见，是说ＪＶＭ里的Perm内存区的异常溢出，由于JVM在默认的情况下，Perm默认为64M，而很多程序需要大量的Perm区内存，尤其使用到像Spring等框架的时候，由于需要使用到动态生成类，而这些类不能被GC自动释放，所以导致OutOfMemoryError: PermGen space异常。解决方法很简单，增大JVM的 -XX:MaxPermSize 启动参数，就可以解决这个问题，如过使用的是默认变量通常是64M[5.0 and newer: 64 bit VMs are scaled 30% larger; 1.4 amd64: 96m; 1.3.1 -client: 32m.]，改成128M就可以了，-XX:MaxPermSize=128m。如果已经是128m（Eclipse已经是128m了），就改成 256m。我一般在服务器上为安全起见，改成256m。\n\njava.lang.OutOfMemoryError：heap space或 其它OutOfMemoryError，这个异常实际上跟上面的异常是一个异常，但解决方法不同，所以分开来写。上面那个异常是因为JVM的perm区内存区分少了引起的（JVM的内存区分为 young,old,perm三种）。而这个异常是因为JVM堆内存或者说总体分少了。解决方法是更改 -Xms -Xmx 启动参数，通常是扩大1倍。xms是管理启动时最小内存量的，xmx是管里JVM最大的内存量的。\n\n注：OutOfMemoryError可能有很多种原因，根据JVM Specification, 可能有一下几种情况，我先简单列出。stack：stack分区不能动态扩展，或不足以生成新的线程。Heap:需要更多的内存，而不能获得。Method Area :如果不能满足分配需求。runtime constant pool(从Method Area分配内存)不足以创建class or interface。native method stacks不能够动态扩展，或生成新的本地线程。\n\n最后说说java.lang.StackOverflowError，老实说这个异常我也没碰见过，但JVM Specification就提一下，规范上说有一下几种境况可能抛出这个异常，一个是Stacks里的线程超过允许的时候，另一个是当native method要求更大的内存，而超过native method允许的内存的时候。根据SUN的文档，提高-XX:ThreadStackSize=512的值。\n\n总的来说调优JVM的内存，组要目的就是在使用内存尽可能小的，使程序运行正常，不抛出内纯溢出的bug。而且要调好最小内存，最大内存的比，避免GC时浪费太多时间，尤其是要尽量避免FULL GC。', 'JVM调优并解决OutOfMemoryError，StackOverflowError', '1', 'Coriger', '2018-08-01 23:53:40', null, '147'), ('255897', '9999', 'JB.Gingerbread-Blog简介篇', '## 技术栈\n\n### 后端\n\n- spring 服务\n- spring boot MVC\n- mybatis ORM\n- spring security 认证\n- redis（后续准备使用）\n\n### 前端\n\n- bootstrap\n- flavr（弹出层）\n- hexo主题\n- editor.md (markdown编辑器)\n- 部分jquery插件\n- thymeleaf模板', '本文主要介绍自己的开源博客相关的情况和使用到的技术，希望有兴趣的朋友可以加入我一起去完善这个项目。', '0', 'JB.Gingerbread', '2017-05-18 19:57:17', '2017-07-10 17:31:39', '526'), ('256166', '10001', 'SpringBoot启动结构图', '[SpringBoot启动结构图](https://www.processon.com/view/link/59812124e4b0de2518b32b6e \"SpringBoot启动结构图\")\n\n[![](https://upload-images.jianshu.io/upload_images/6912735-51aa162747fcdc3d.png?imageMogr2/auto-orient/strip)](https://upload-images.jianshu.io/upload_images/6912735-51aa162747fcdc3d.png?imageMogr2/auto-orient/strip)', 'SpringBoot启动结构图', '1', 'Coriger', '2019-01-16 18:25:13', null, '57'), ('256527', '10001', 'RocketMQ的RPC通信', 'RocketMQ的RPC通信\n\n一、RocketMQ中Remoting通信模块概览\nRocketMQ消息队列的整体部署架构如下图所示：\n[![](http://p3.pstatp.com/large/pgc-image/1531625221230db8d313beb)](http://p3.pstatp.com/large/pgc-image/1531625221230db8d313beb)\n先来说下RocketMQ消息队列集群中的几个角色：\n\n（1）NameServer：在MQ集群中做的是做命名服务，更新和路由发现 broker服务；\n\n（2）Broker-Master：broker 消息主机服务器；\n\n（3）Broker-Slave：broker 消息从机服务器；\n\n（4）Producer：消息生产者；\n\n（5）Consumer：消息消费者；\n\n其中，RocketMQ集群的一部分通信如下：\n\n（1）Broker启动后需要完成一次将自己注册至NameServer的操作；随后每隔30s时间定期向NameServer上报Topic路由信息；\n\n（2）消息生产者Producer作为客户端发送消息时候，需要根据Msg的Topic从本地缓存的TopicPublishInfoTable获取路由信息。如果没有则更新路由信息会从NameServer上重新拉取；\n\n（3）消息生产者Producer根据（2）中获取的路由信息选择一个队列（MessageQueue）进行消息发送；Broker作为消息的接收者收消息并落盘存储； 从上面（1）~（3）中可以看出在消息生产者, Broker和NameServer之间都会发生通信（这里只说了MQ的部分通信），因此如何设计一个良好的网络通信模块在MQ中至关重要，它将决定RocketMQ集群整体的消息传输能力与最终的性能。\n\nrocketmq-remoting 模块是 RocketMQ消息队列中负责网络通信的模块，它几乎被其他所有需要网络通信的模块（诸如rocketmq-client、rocketmq-server、rocketmq-namesrv）所依赖和引用。为了实现客户端与服务器之间高效的数据请求与接收，RocketMQ消息队列自定义了通信协议并在Netty的基础之上扩展了通信模块。ps:鉴于RocketMQ的通信模块是建立在Netty基础之上的，因此在阅读RocketMQ的源码之前，读者最好先对Netty的多线程模型、JAVA NIO模型均有一定的了解，这样子理解RocketMQ源码会较为快一些。\n\n作者阅读的RocketMQ版本是4.2.0, 依赖的netty版本是4.0.42.Final. RocketMQ的代码结构图如下:\n\n[![](http://p1.pstatp.com/large/pgc-image/1531625221102968ae1998f)](http://p1.pstatp.com/large/pgc-image/1531625221102968ae1998f)\n源码部分主要可以分为rocketmq-broker，rocketmq-client，rocketmq-common，rocketmq-filterSrv，rocketmq-namesrv和rocketmq-remoting等模块，通信框架就封装在rocketmq-remoting模块中。 本文主要从RocketMQ的协议格式，消息编解码，通信方式(同步/异步/单向)和具体的发送/接收消息的通信流程来进行阐述等。\n\n二、RocketMQ中Remoting通信模块的具体实现\nRemoting通信模块的类结构图\n[![](http://p3.pstatp.com/large/pgc-image/1531625221210a67c7e50aa)](http://p3.pstatp.com/large/pgc-image/1531625221210a67c7e50aa)\n从类层次结构来看：\n\n（1）RemotingService：为最上层的接口，提供了三个方法：\n\nvoid\nstart();\nvoid\nshutdown();\nvoid\nregisterRPCHook(\nRPCHook\nrpcHook);\n（2）RemotingClient/RemotingSever：两个接口继承了最上层接口—RemotingService，分别各自为Client和Server提供所必需的方法，下面所列的是RemotingServer的方法：\n\n/**\n* 同RemotingClient端一样\n*\n* @param requestCode\n* @param processor\n* @param executor\n*/\nvoid\nregisterProcessor(\nfinal\nint\nrequestCode,\nfinal\nNettyRequestProcessor\nprocessor,\nfinal\nExecutorService\nexecutor);\n/**\n* 注册默认的处理器\n*\n* @param processor\n* @param executor\n*/\nvoid\nregisterDefaultProcessor(\nfinal\nNettyRequestProcessor\nprocessor,\nfinal\nExecutorService\nexecutor);\nint\nlocalListenPort();\n/**\n* 根据请求code来获取不同的处理Pair\n*\n* @param requestCode\n* @return\n*/\nPair\n<\nNettyRequestProcessor\n,\nExecutorService\n> getProcessorPair(\nfinal\nint\nrequestCode);\n/**\n* 同RemotingClient端一样,同步通信,有返回RemotingCommand\n* @param channel\n* @param request\n* @param timeoutMillis\n* @return\n* @throws InterruptedException\n* @throws RemotingSendRequestException\n* @throws RemotingTimeoutException\n*/\nRemotingCommand\ninvokeSync(\nfinal\nChannel\nchannel,\nfinal\nRemotingCommand\nrequest,\nfinal\nlong\ntimeoutMillis)\nthrows\nInterruptedException\n,\nRemotingSendRequestException\n,\nRemotingTimeoutException\n;\n/**\n* 同RemotingClient端一样,异步通信,无返回RemotingCommand\n*\n* @param channel\n* @param request\n* @param timeoutMillis\n* @param invokeCallback\n* @throws InterruptedException\n* @throws RemotingTooMuchRequestException\n* @throws RemotingTimeoutException\n* @throws RemotingSendRequestException\n*/\nvoid\ninvokeAsync(\nfinal\nChannel\nchannel,\nfinal\nRemotingCommand\nrequest,\nfinal\nlong\ntimeoutMillis,\nfinal\nInvokeCallback\ninvokeCallback)\nthrows\nInterruptedException\n,\nRemotingTooMuchRequestException\n,\nRemotingTimeoutException\n,\nRemotingSendRequestException\n;\n/**\n* 同RemotingClient端一样，单向通信，诸如心跳包\n*\n* @param channel\n* @param request\n* @param timeoutMillis\n* @throws InterruptedException\n* @throws RemotingTooMuchRequestException\n* @throws RemotingTimeoutException\n* @throws RemotingSendRequestException\n*/\nvoid\ninvokeOneway(\nfinal\nChannel\nchannel,\nfinal\nRemotingCommand\nrequest,\nfinal\nlong\ntimeoutMillis)\nthrows\nInterruptedException\n,\nRemotingTooMuchRequestException\n,\nRemotingTimeoutException\n,\nRemotingSendRequestException\n;\n（3）NettyRemotingAbstract：Netty通信处理的抽象类，定义并封装了Netty处理的公共处理方法；\n\n（4）NettyRemotingClient以及NettyRemotingServer：分别实现了RemotingClient和RemotingServer, 都继承了NettyRemotingAbstract抽象类。RocketMQ中其他的组件（如client、nameServer、broker在进行消息的发送和接收时均使用这两个组件）\n\n消息的协议设计与编码解码\n在Client和Server之间完成一次消息发送时，需要对发送的消息进行一个协议约定，因此就有必要自定义RocketMQ的消息协议。同时，为了高效地在网络中传输消息和对收到的消息读取，就需要对消息进行编解码。在RocketMQ中，RemotingCommand这个类在消息传输过程中对所有数据内容的封装，不但包含了所有的数据结构，还包含了编码解码操作。\n\nRemotingCommand类的部分成员变量如下：\n\nHeader字段类型Request说明Response说明codeint请求操作码，应答方根据不同的请求码进行不同的业务处理应答响应码。0表示成功，非0则表示各种错误languageLanguageCode请求方实现的语言应答方实现的语言versionint请求方程序的版本应答方程序的版本opaqueint相当于reqeustId，在同一个连接上的不同请求标识码，与响应消息中的相对应应答不做修改直接返回flagint区分是普通RPC还是onewayRPC得标志区分是普通RPC还是onewayRPC得标志remarkString传输自定义文本信息传输自定义文本信息extFieldsHashMap请求自定义扩展信息响应自定义扩展信息\n\n这里展示下Broker向NameServer发送一次心跳注册的报文：\n\n[\ncode=\n103\n,\n//这里的103对应的code就是broker向nameserver注册自己的消息\nlanguage=JAVA,\nversion=\n137\n,\nopaque=\n58\n,\n//这个就是requestId\nflag(B)=\n0\n,\nremark=\nnull\n,\nextFields={\nbrokerId=\n0\n,\nclusterName=\nDefaultCluster\n,\nbrokerAddr=ip1:\n10911\n,\nhaServerAddr=ip1:\n10912\n,\nbrokerName=LAPTOP-SMF2CKDN\n},\nserializeTypeCurrentRPC=JSON\n下面来看下RocketMQ通信协议的格式：\n\n消息中间件—RocketMQ的RPC通信（一）\n可以看到传输内容主要可以分为以下4部分：\n\n（1）消息长度：总长度，四个字节存储，占用一个int类型；\n\n（2）序列化类型&消息头长度：同样占用一个int类型，第一个字节表示序列化类型，后面三个字节表示消息头长度；\n\n（3）消息头数据：经过序列化后的消息头数据；\n\n（4）消息主体数据：消息主体的二进制字节数据内容；\n\n消息的编码和解码分别在RemotingCommand类的encode和decode方法中完成，下面是消息编码encode方法的具体实现：\n\npublic\nByteBuffer\nencode() {\n// 1> header length size\nint\nlength =\n4\n;\n//消息总长度\n// 2> header data length\n//将消息头编码成byte[]\nbyte\n[] headerData =\nthis\n.headerEncode();\n//计算头部长度\nlength += headerData.length;\n// 3> body data length\nif\n(\nthis\n.body !=\nnull\n) {\n//消息主体长度\nlength += body.length;\n}\n//分配ByteBuffer, 这边加了4,\n//这是因为在消息总长度的计算中没有将存储头部长度的4个字节计算在内\nByteBuffer\nresult =\nByteBuffer\n.allocate(\n4\n+ length);\n// length\n//将消息总长度放入ByteBuffer\nresult.putInt(length);\n// header length\n//将消息头长度放入ByteBuffer\nresult.put(markProtocolType(headerData.length, serializeTypeCurrentRPC));\n// header data\n//将消息头数据放入ByteBuffer\nresult.put(headerData);\n// body data;\nif\n(\nthis\n.body !=\nnull\n) {\n//将消息主体放入ByteBuffer\nresult.put(\nthis\n.body);\n}\n//重置ByteBuffer的position位置\nresult.flip();\nreturn\nresult;\n}\n/**\n* markProtocolType方法是将RPC类型和headerData长度编码放到一个byte[4]数组中\n*\n* @param source\n* @param type\n* @return\n*/\npublic\nstatic\nbyte\n[] markProtocolType(\nint\nsource,\nSerializeType\ntype) {\nbyte\n[] result =\nnew\nbyte\n[\n4\n];\nresult[\n0\n] = type.getCode();\n//右移16位后再和255与->“16-24位”\nresult[\n1\n] = (\nbyte\n) ((source >>\n16\n) &\n0xFF\n);\n//右移8位后再和255与->“8-16位”\nresult[\n2\n] = (\nbyte\n) ((source >>\n8\n) &\n0xFF\n);\n//右移0位后再和255与->“8-0位”\nresult[\n3\n] = (\nbyte\n) (source &\n0xFF\n);\nreturn\nresult;\n}\n消息解码decode方法是编码的逆向过程，其具体实现如下：\n\npublic\nstatic\nRemotingCommand\ndecode(\nfinal\nByteBuffer\nbyteBuffer) {\n//获取byteBuffer的总长度\nint\nlength = byteBuffer.limit();\n//获取前4个字节，组装int类型，该长度为总长度\nint\noriHeaderLen = byteBuffer.getInt();\n//获取消息头的长度，这里和0xFFFFFF做与运算，编码时候的长度即为24位\nint\nheaderLength = getHeaderLength(oriHeaderLen);\nbyte\n[] headerData =\nnew\nbyte\n[headerLength];\nbyteBuffer.\nget\n(headerData);\nRemotingCommand\ncmd = headerDecode(headerData, getProtocolType(oriHeaderLen));\nint\nbodyLength = length -\n4\n- headerLength;\nbyte\n[] bodyData =\nnull\n;\nif\n(bodyLength >\n0\n) {\nbodyData =\nnew\nbyte\n[bodyLength];\nbyteBuffer.\nget\n(bodyData);\n}\ncmd.body = bodyData;\nreturn\ncmd;\n}\n消息的通信方式和通信流程\n在RocketMQ消息队列中支持通信的方式主要有以下三种：\n\n（1）同步(sync)\n\n（2）异步(async)\n\n（3）单向(oneway)\n\n其中“同步”通信模式相对简单，一般用在发送心跳包场景下，无需关注其Response。本文将主要介绍RocketMQ的异步通信流程（限于篇幅，读者可以按照同样的模式进行分析同步通信流程）。下面先给出了RocketMQ异步通信的整体流程图：\n\n[![](http://p3.pstatp.com/large/pgc-image/15316252212991174485368)](http://p3.pstatp.com/large/pgc-image/15316252212991174485368)\n下面两小节内容主要介绍了Client端发送请求消息、Server端接收消息的具体实现并简要分析的Client端的回调。\n\n1）Client发送请求消息的具体实现\n\n当客户端调用异步通信接口—invokeAsync时候，先由RemotingClient的实现类—NettyRemotingClient根据addr获取相应的channel（如果本地缓存中没有则创建），随后调用invokeAsyncImpl方法，将数据流转给抽象类NettyRemotingAbstract处理（真正做完发送请求动作的是在NettyRemotingAbstract抽象类的invokeAsyncImpl方法里面）。\n\n具体发送请求消息的源代码如下所示：\n\n/**\n* invokeAsync（异步调用）\n*\n*/\npublic\nvoid\ninvokeAsyncImpl(\nfinal\nChannel\nchannel,\nfinal\nRemotingCommand\nrequest,\nfinal\nlong\ntimeoutMillis,\nfinal\nInvokeCallback\ninvokeCallback)\nthrows\nInterruptedException\n,\nRemotingTooMuchRequestException\n,\nRemotingTimeoutException\n,\nRemotingSendRequestException\n{\n//相当于request ID, RemotingCommand会为每一个request产生一个request ID, 从0开始, 每次加1\nfinal\nint\nopaque = request.getOpaque();\nboolean\nacquired =\nthis\n.semaphoreAsync.tryAcquire(timeoutMillis,\nTimeUnit\n.MILLISECONDS);\nif\n(acquired) {\nfinal\nSemaphoreReleaseOnlyOnce\nonce =\nnew\nSemaphoreReleaseOnlyOnce\n(\nthis\n.semaphoreAsync);\n//根据request ID构建ResponseFuture\nfinal\nResponseFuture\nresponseFuture =\nnew\nResponseFuture\n(opaque, timeoutMillis, invokeCallback, once);\n//将ResponseFuture放入responseTable\nthis\n.responseTable.put(opaque, responseFuture);\ntry\n{\n//使用Netty的channel发送请求数据\nchannel.writeAndFlush(request).addListener(\nnew\nChannelFutureListener\n() {\n//消息发送后执行\n@Override\npublic\nvoid\noperationComplete(\nChannelFuture\nf)\nthrows\nException\n{\nif\n(f.isSuccess()) {\n//如果发送消息成功给Server，那么这里直接Set后return\nresponseFuture.setSendRequestOK(\ntrue\n);\nreturn\n;\n}\nelse\n{\nresponseFuture.setSendRequestOK(\nfalse\n);\n}\nresponseFuture.putResponse(\nnull\n);\nresponseTable.remove(opaque);\ntry\n{\n//执行回调\nexecuteInvokeCallback(responseFuture);\n}\ncatch\n(\nThrowable\ne) {\nlog.warn(\n\"excute callback in writeAndFlush addListener, and callback throw\"\n, e);\n}\nfinally\n{\n//释放信号量\nresponseFuture.release();\n}\nlog.warn(\n\"send a request command to channel <{}> failed.\"\n,\nRemotingHelper\n.parseChannelRemoteAddr(channel));\n}\n});\n}\ncatch\n(\nException\ne) {\n//异常处理\nresponseFuture.release();\nlog.warn(\n\"send a request command to channel <\"\n+\nRemotingHelper\n.parseChannelRemoteAddr(channel) +\n\"> Exception\"\n, e);\nthrow\nnew\nRemotingSendRequestException\n(\nRemotingHelper\n.parseChannelRemoteAddr(channel), e);\n}\n}\nelse\n{\nif\n(timeoutMillis <=\n0\n) {\nthrow\nnew\nRemotingTooMuchRequestException\n(\n\"invokeAsyncImpl invoke too fast\"\n);\n}\nelse\n{\nString\ninfo =\nString\n.format(\n\"invokeAsyncImpl tryAcquire semaphore timeout, %dms, waiting thread nums: %d semaphoreAsyncValue: %d\"\n,\ntimeoutMillis,\nthis\n.semaphoreAsync.getQueueLength(),\nthis\n.semaphoreAsync.availablePermits()\n);\nlog.warn(info);\nthrow\nnew\nRemotingTimeoutException\n(info);\n}\n}\n}\n在Client端发送请求消息时有个比较重要的数据结构需要注意下：\n\n（1）responseTable—保存请求码与响应关联映射\n\nprotected\nfinal\nConcurrentHashMap\n<\nInteger\n/* opaque */\n,\nResponseFuture\n> responseTable\nopaque表示请求发起方在同个连接上不同的请求标识代码，每次发送一个消息的时候，可以选择同步阻塞/异步非阻塞的方式。无论是哪种通信方式，都会保存请求操作码至ResponseFuture的Map映射—responseTable中。\n\n（2）ResponseFuture—保存返回响应（包括回调执行方法和信号量）\n\npublic\nResponseFuture\n(\nint\nopaque,\nlong\ntimeoutMillis,\nInvokeCallback\ninvokeCallback,\nSemaphoreReleaseOnlyOnce\nonce) {\nthis\n.opaque = opaque;\nthis\n.timeoutMillis = timeoutMillis;\nthis\n.invokeCallback = invokeCallback;\nthis\n.once = once;\n}\n对于同步通信来说，第三、四个参数为null；而对于异步通信来说，invokeCallback是在收到消息响应的时候能够根据responseTable找到请求码对应的回调执行方法，semaphore参数用作流控，当多个线程同时往一个连接写数据时可以通过信号量控制permit同时写许可的数量。\n\n（3）异常发送流程处理—定时扫描responseTable本地缓存\n\n在发送消息时候，如果遇到异常情况（比如服务端没有response返回给客户端或者response因网络而丢失），上面所述的responseTable的本地缓存Map将会出现堆积情况。这个时候需要一个定时任务来专门做responseTable的清理回收。在RocketMQ的客户端/服务端启动时候会产生一个频率为1s调用一次来的定时任务检查所有的responseTable缓存中的responseFuture变量，判断是否已经得到返回, 并进行相应的处理。\n\npublic\nvoid\nscanResponseTable() {\nfinal\nList\n<\nResponseFuture\n> rfList =\nnew\nLinkedList\n<\nResponseFuture\n>();\nIterator\n<\nEntry\n<\nInteger\n,\nResponseFuture\n>> it =\nthis\n.responseTable.entrySet().iterator();\nwhile\n(it.hasNext()) {\nEntry\n<\nInteger\n,\nResponseFuture\n>\nnext\n= it.\nnext\n();\nResponseFuture\nrep =\nnext\n.getValue();\nif\n((rep.getBeginTimestamp() + rep.getTimeoutMillis() +\n1000\n) <=\nSystem\n.currentTimeMillis()) {\nrep.release();\nit.remove();\nrfList.add(rep);\nlog.warn(\n\"remove timeout request, \"\n+ rep);\n}\n}\nfor\n(\nResponseFuture\nrf : rfList) {\ntry\n{\nexecuteInvokeCallback(rf);\n}\ncatch\n(\nThrowable\ne) {\nlog.warn(\n\"scanResponseTable, operationComplete Exception\"\n, e);\n}\n}\n}\n2）Server端接收消息并进行处理的具体实现\n\nServer端接收消息的处理入口在NettyServerHandler类的channelRead0方法中，其中调用了processMessageReceived方法（这里省略了Netty服务端消息流转的大部分流程和逻辑）。\n\n其中服务端最为重要的处理请求方法实现如下：\n\npublic\nvoid\nprocessRequestCommand(\nfinal\nChannelHandlerContext\nctx,\nfinal\nRemotingCommand\ncmd) {\n//根据RemotingCommand中的code获取processor和ExecutorService\nfinal\nPair\n<\nNettyRequestProcessor\n,\nExecutorService\n> matched =\nthis\n.processorTable.\nget\n(cmd.getCode());\nfinal\nPair\n<\nNettyRequestProcessor\n,\nExecutorService\n> pair =\nnull\n== matched ?\nthis\n.defaultRequestProcessor : matched;\nfinal\nint\nopaque = cmd.getOpaque();\nif\n(pair !=\nnull\n) {\nRunnable\nrun =\nnew\nRunnable\n() {\n@Override\npublic\nvoid\nrun() {\ntry\n{\n//rpc hook\nRPCHook\nrpcHook =\nNettyRemotingAbstract\n.\nthis\n.getRPCHook();\nif\n(rpcHook !=\nnull\n) {\nrpcHook.doBeforeRequest(\nRemotingHelper\n.parseChannelRemoteAddr(ctx.channel()), cmd);\n}\n//processor处理请求\nfinal\nRemotingCommand\nresponse = pair.getObject1().processRequest(ctx, cmd);\n//rpc hook\nif\n(rpcHook !=\nnull\n) {\nrpcHook.doAfterResponse(\nRemotingHelper\n.parseChannelRemoteAddr(ctx.channel()), cmd, response);\n}\nif\n(!cmd.isOnewayRPC()) {\nif\n(response !=\nnull\n) {\nresponse.setOpaque(opaque);\nresponse.markResponseType();\ntry\n{\nctx.writeAndFlush(response);\n}\ncatch\n(\nThrowable\ne) {\nPLOG.error(\n\"process request over, but response failed\"\n, e);\nPLOG.error(cmd.toString());\nPLOG.error(response.toString());\n}\n}\nelse\n{\n}\n}\n}\ncatch\n(\nThrowable\ne) {\nif\n(!\n\"com.aliyun.openservices.ons.api.impl.authority.exception.AuthenticationException\"\n.equals(e.getClass().getCanonicalName())) {\nPLOG.error(\n\"process request exception\"\n, e);\nPLOG.error(cmd.toString());\n}\nif\n(!cmd.isOnewayRPC()) {\nfinal\nRemotingCommand\nresponse =\nRemotingCommand\n.createResponseCommand(\nRemotingSysResponseCode\n.SYSTEM_ERROR,\n//\nRemotingHelper\n.exceptionSimpleDesc(e));\nresponse.setOpaque(opaque);\nctx.writeAndFlush(response);\n}\n}\n}\n};\nif\n(pair.getObject1().rejectRequest()) {\nfinal\nRemotingCommand\nresponse =\nRemotingCommand\n.createResponseCommand(\nRemotingSysResponseCode\n.SYSTEM_BUSY,\n\"[REJECTREQUEST]system busy, start flow control for a while\"\n);\nresponse.setOpaque(opaque);\nctx.writeAndFlush(response);\nreturn\n;\n}\ntry\n{\n//封装requestTask\nfinal\nRequestTask\nrequestTask =\nnew\nRequestTask\n(run, ctx.channel(), cmd);\n//想线程池提交requestTask\npair.getObject2().submit(requestTask);\n}\ncatch\n(\nRejectedExecutionException\ne) {\nif\n((\nSystem\n.currentTimeMillis() %\n10000\n) ==\n0\n) {\nPLOG.warn(\nRemotingHelper\n.parseChannelRemoteAddr(ctx.channel())\n//\n+\n\", too many requests and system thread pool busy, RejectedExecutionException \"\n//\n+ pair.getObject2().toString()\n//\n+\n\" request code: \"\n+ cmd.getCode());\n}\nif\n(!cmd.isOnewayRPC()) {\nfinal\nRemotingCommand\nresponse =\nRemotingCommand\n.createResponseCommand(\nRemotingSysResponseCode\n.SYSTEM_BUSY,\n\"[OVERLOAD]system busy, start flow control for a while\"\n);\nresponse.setOpaque(opaque);\nctx.writeAndFlush(response);\n}\n}\n}\nelse\n{\nString\nerror =\n\" request type \"\n+ cmd.getCode() +\n\" not supported\"\n;\n//构建response\nfinal\nRemotingCommand\nresponse =\nRemotingCommand\n.createResponseCommand(\nRemotingSysResponseCode\n.REQUEST_CODE_NOT_SUPPORTED, error);\nresponse.setOpaque(opaque);\nctx.writeAndFlush(response);\nPLOG.error(\nRemotingHelper\n.parseChannelRemoteAddr(ctx.channel()) + error);\n}\n}\n上面的请求处理方法中根据RemotingCommand的请求业务码来匹配到相应的业务处理器；然后生成一个新的线程提交至对应的业务线程池进行异步处理。\n\n（1）processorTable—请求业务码与业务处理、业务线程池的映射变量\n\nprotected\nfinal\nHashMap\n<\nInteger\n/* request code */\n,\nPair\n<\nNettyRequestProcessor\n,\nExecutorService\n>> processorTable =\nnew\nHashMap\n<\nInteger\n,\nPair\n<\nNettyRequestProcessor\n,\nExecutorService\n>>(\n64\n);\n个人觉得RocketMQ这种做法是为了给不同类型的请求业务码指定不同的处理器Processor处理，同时消息实际的处理并不是在当前线程，而是被封装成task放到业务处理器Processor对应的线程池中完成异步执行。(在RocketMQ中能看到很多地方都是这样的处理，这样的设计能够最大程度的保证异步，保证每个线程都专注处理自己负责的东西）\n\n3）Client端异步回调执行的实现分析\n\n看到这里可能有一些同学会疑问Client端的异步回调究竟在哪里执行的？从上面“RocketMQ异步通信的整体时序图”来看，回调执行处理的流程的确是放在了Client端来完成，而rocketmq-remoting通信模块中只是给异步回调处理提供了接口。\n\n这里可以看下rocketmq-client模块异步发送消息的部分代码（限于篇幅也只是列举了异步回调执行的部分代码）：\n\nprivate\nvoid\nsendMessageAsync(\nfinal\nString\naddr,\nfinal\nString\nbrokerName,\nfinal\nMessage\nmsg,\nfinal\nlong\ntimeoutMillis,\nfinal\nRemotingCommand\nrequest,\nfinal\nSendCallback\nsendCallback,\nfinal\nTopicPublishInfo\ntopicPublishInfo,\nfinal\nMQClientInstance\ninstance,\nfinal\nint\nretryTimesWhenSendFailed,\nfinal\nAtomicInteger\ntimes,\nfinal\nSendMessageContext\ncontext,\nfinal\nDefaultMQProducerImpl\nproducer\n)\nthrows\nInterruptedException\n,\nRemotingException\n{\nthis\n.remotingClient.invokeAsync(addr, request, timeoutMillis,\nnew\nInvokeCallback\n() {\n@Override\npublic\nvoid\noperationComplete(\nResponseFuture\nresponseFuture) {\n//先从Server端返回的responseFuture变量中获取RemotingCommand的值\nRemotingCommand\nresponse = responseFuture.getResponseCommand();\nif\n(\nnull\n== sendCallback && response !=\nnull\n) {\ntry\n{\n//Client端处理发送消息的Reponse返回（包括对消息返回体的头部进行解码，\n//取得“topic”、“BrokerName”、“QueueId”等值）\n//随后构建sendResult对象并设置Context上下文中\nSendResult\nsendResult =\nMQClientAPIImpl\n.\nthis\n.processSendResponse(brokerName, msg, response);\nif\n(context !=\nnull\n&& sendResult !=\nnull\n) {\ncontext.setSendResult(sendResult);\ncontext.getProducer().executeSendMessageHookAfter(context);\n}\n}\ncatch\n(\nThrowable\ne) {\n}\nproducer.updateFaultItem(brokerName,\nSystem\n.currentTimeMillis() - responseFuture.getBeginTimestamp(),\nfalse\n);\nreturn\n;\n}\n//省略其他部分代码\n//......\n}\n这里需要结合3.1节的内容和NettyRemotingAbstract抽象类的processResponseCommand方法，便可以明白Client端实现异步回调的大致流程了。在Client端发送异步消息时候（rocketmq-client模块最终调用sendMessageAsync方法时），会将InvokeCallback的接口注入，而在Server端的异步线程由上面所讲的业务线程池真正执行后，返回response给Client端时候才会去触发执行。\n\nNettyRemotingAbstract抽象类的processResponseCommand方法的具体代码如下：\n\npublic\nvoid\nprocessResponseCommand(\nChannelHandlerContext\nctx,\nRemotingCommand\ncmd) {\n//从RemotingCommand中获取opaque值\nfinal\nint\nopaque = cmd.getOpaque();‘\n//从本地缓存的responseTable这个Map中取出本次异步通信连接对应的ResponseFuture变量\nfinal\nResponseFuture\nresponseFuture = responseTable.\nget\n(opaque);\nif\n(responseFuture !=\nnull\n) {\nresponseFuture.setResponseCommand(cmd);\nresponseTable.remove(opaque);\nif\n(responseFuture.getInvokeCallback() !=\nnull\n) {\n//在这里真正去执行Client注入进来的异步回调方法\nexecuteInvokeCallback(responseFuture);\n}\nelse\n{\n//否则释放responseFuture变量\nresponseFuture.putResponse(cmd);\nresponseFuture.release();\n}\n}\nelse\n{\nlog.warn(\n\"receive response, but not matched any request, \"\n+\nRemotingHelper\n.parseChannelRemoteAddr(ctx.channel()));\nlog.warn(cmd.toString());\n}\n}\n为何要使用Netty作为高性能的通信库？\n\n在看RocketMQ的RPC通信部分时候，可能有不少同学有这样子的疑问，RocketMQ为何要选择Netty而不直接使用JDK的NIO进行网络编程呢？这里有必要先来简要介绍下Netty。 Netty是一个封装了JDK的NIO库的高性能网络通信开源框架。它提供异步的、事件驱动的网络应用程序框架和工具，用以快速开发高性能、高可靠性的网络服务器和客户端程序。\n\n下面主要列举了下一般系统的RPC通信模块会选择Netty作为底层通信库的理由（作者认为RocketMQ的RPC同样也是基于此选择了Netty）：\n\n（1）Netty的编程API使用简单，开发门槛低，无需编程者去关注和了解太多的NIO编程模型和概念；\n\n（2）对于编程者来说，可根据业务的要求进行定制化地开发，通过Netty的ChannelHandler对通信框架进行灵活的定制化扩展；\n\n（3）Netty框架本身支持拆包/解包，异常检测等机制，让编程者可以从JAVA NIO的繁琐细节中解脱，而只需要关注业务处理逻辑；\n\n（4）Netty解决了（准确地说应该是采用了另一种方式完美规避了）JDK NIO的Bug（Epoll bug，会导致Selector空轮询，最终导致CPU 100%）；\n\n（5）Netty框架内部对线程，selector做了一些细节的优化，精心设计的reactor多线程模型，可以实现非常高效地并发处理；\n\n（6）Netty已经在多个开源项目（Hadoop的RPC框架avro使用Netty作为通信框架）中都得到了充分验证，健壮性/可靠性比较好。\n\nRocketMQ中RPC通信的Netty多线程模型\n\nRocketMQ的RPC通信部分采用了\"1+N+M1+M2\"的Reactor多线程模式，对网络通信部分进行了一定的扩展与优化，这一节主要让我们来看下这一部分的具体设计与实现内容。\n\nNetty的Reactor多线程模型设计概念与简述\n这里有必要先来简要介绍下Netty的Reactor多线程模型。Reactor多线程模型的设计思想是分而治之+事件驱动。\n\n（1）分而治之\n\n一般来说，一个网络请求连接的完整处理过程可以分为接受（accept）、数据读取（read）、解码/编码（decode/encode）、业务处理（process）、发送响应（send）这几步骤。Reactor模型将每个步骤都映射成为一个任务，服务端线程执行的最小逻辑单元不再是一次完整的网络请求，而是这个任务，且采用以非阻塞方式执行。\n\n（2）事件驱动\n\n每个任务对应特定网络事件。当任务准备就绪时，Reactor收到对应的网络事件通知，并将任务分发给绑定了对应网络事件的Handler执行。\n\nRocketMQ中RPC通信的1+N+M1+M2的Reactor多线程设计与实现\n（1）RocketMQ中RPC通信的Reactor多线程设计与流程\n\nRocketMQ的RPC通信采用Netty组件作为底层通信库，同样也遵循了Reactor多线程模型，同时又在这之上做了一些扩展和优化。下面先给出一张RocketMQ的RPC通信层的Netty多线程模型框架图，让大家对RocketMQ的RPC通信中的多线程分离设计有一个大致的了解。\n\n[![](http://p3.pstatp.com/large/pgc-image/153162572556289a3ea842b)](http://p3.pstatp.com/large/pgc-image/153162572556289a3ea842b)\n从上面的框图中可以大致了解RocketMQ中NettyRemotingServer的Reactor 多线程模型。一个 Reactor 主线程（eventLoopGroupBoss，即为上面的1）负责监听 TCP网络连接请求，建立好连接后丢给Reactor 线程池（eventLoopGroupSelector，即为上面的“N”，源码中默认设置为3），它负责将建立好连接的socket 注册到 selector上去（RocketMQ的源码中会自动根据OS的类型选择NIO和Epoll，也可以通过参数配置），然后监听真正的网络数据。拿到网络数据后，再丢给Worker线程池（defaultEventExecutorGroup，即为上面的“M1”，源码中默认设置为8）。\n\n为了更为高效的处理RPC的网络请求，这里的Worker线程池是专门用于处理Netty网络通信相关的（包括编码/解码、空闲链接管理、网络连接管理以及网络请求处理）。而处理业务操作放在业务线程池中执行（这个内容在“RocketMQ的RPC通信（一）篇”中也有提到），根据 RomotingCommand 的业务请求码code去processorTable这个本地缓存变量中找到对应的 processor，然后封装成task任务后，提交给对应的业务processor处理线程池来执行（sendMessageExecutor，以发送消息为例，即为上面的 “M2”）。\n\n下面以表格的方式列举了下上面所述的“1+N+M1+M2”Reactor多线程模型\n[![](http://p3.pstatp.com/large/pgc-image/1531625725356e73349e2f7)](http://p3.pstatp.com/large/pgc-image/1531625725356e73349e2f7)\n（2）RocketMQ中RPC通信的Reactor多线程的代码具体实现\n\n说完了Reactor多线程整体的设计与流程，大家应该就对RocketMQ的RPC通信的Netty部分有了一个比较全面的理解了，那接下来就从源码上来看下一些细节部分（在看该部分代码时候需要读者对JAVA NIO和Netty的相关概念与技术点有所了解）。在NettyRemotingServer的实例初始化时，会初始化各个相关的变量包括serverBootstrap、nettyServerConfig参数、channelEventListener监听器并同时初始化eventLoopGroupBoss和eventLoopGroupSelector两个Netty的EventLoopGroup线程池（这里需要注意的是，如果是Linux平台，并且开启了native epoll，就用EpollEventLoopGroup，这个也就是用JNI，调的c写的epoll；否则，就用Java NIO的NioEventLoopGroup。）。\n\n具体代码如下：\n\npublic\nNettyRemotingServer\n(\nfinal\nNettyServerConfig\nnettyServerConfig,\nfinal\nChannelEventListener\nchannelEventListener) {\nsuper\n(nettyServerConfig.getServerOnewaySemaphoreValue(), nettyServerConfig.getServerAsyncSemaphoreValue());\nthis\n.serverBootstrap =\nnew\nServerBootstrap\n();\nthis\n.nettyServerConfig = nettyServerConfig;\nthis\n.channelEventListener = channelEventListener;\n//省略部分代码\n//初始化时候nThreads设置为1,说明RemotingServer端的Disptacher链接管理和分发请求的线程为1,用于接收客户端的TCP连接\nthis\n.eventLoopGroupBoss =\nnew\nNioEventLoopGroup\n(\n1\n,\nnew\nThreadFactory\n() {\nprivate\nAtomicInteger\nthreadIndex =\nnew\nAtomicInteger\n(\n0\n);\n@Override\npublic\nThread\nnewThread(\nRunnable\nr) {\nreturn\nnew\nThread\n(r,\nString\n.format(\n\"NettyBoss_%d\"\n,\nthis\n.threadIndex.incrementAndGet()));\n}\n});\n/**\n* 根据配置设置NIO还是Epoll来作为Selector线程池\n* 如果是Linux平台，并且开启了native epoll，就用EpollEventLoopGroup，这个也就是用JNI，调的c写的epoll；否则，就用Java NIO的NioEventLoopGroup。\n*\n*/\nif\n(useEpoll()) {\nthis\n.eventLoopGroupSelector =\nnew\nEpollEventLoopGroup\n(nettyServerConfig.getServerSelectorThreads(),\nnew\nThreadFactory\n() {\nprivate\nAtomicInteger\nthreadIndex =\nnew\nAtomicInteger\n(\n0\n);\nprivate\nint\nthreadTotal = nettyServerConfig.getServerSelectorThreads();\n@Override\npublic\nThread\nnewThread(\nRunnable\nr) {\nreturn\nnew\nThread\n(r,\nString\n.format(\n\"NettyServerEPOLLSelector_%d_%d\"\n, threadTotal,\nthis\n.threadIndex.incrementAndGet()));\n}\n});\n}\nelse\n{\nthis\n.eventLoopGroupSelector =\nnew\nNioEventLoopGroup\n(nettyServerConfig.getServerSelectorThreads(),\nnew\nThreadFactory\n() {\nprivate\nAtomicInteger\nthreadIndex =\nnew\nAtomicInteger\n(\n0\n);\nprivate\nint\nthreadTotal = nettyServerConfig.getServerSelectorThreads();\n@Override\npublic\nThread\nnewThread(\nRunnable\nr) {\nreturn\nnew\nThread\n(r,\nString\n.format(\n\"NettyServerNIOSelector_%d_%d\"\n, threadTotal,\nthis\n.threadIndex.incrementAndGet()));\n}\n});\n}\n//省略部分代码\n在NettyRemotingServer实例初始化完成后，就会将其启动。Server端在启动阶段会将之前实例化好的1个acceptor线程（eventLoopGroupBoss），N个IO线程（eventLoopGroupSelector），M1个worker 线程（defaultEventExecutorGroup）绑定上去。前面部分也已经介绍过各个线程池的作用了。\n\n这里需要说明的是，Worker线程拿到网络数据后，就交给Netty的ChannelPipeline（其采用责任链设计模式），从Head到Tail的一个个Handler执行下去，这些 Handler是在创建NettyRemotingServer实例时候指定的。NettyEncoder和NettyDecoder 负责网络传输数据和 RemotingCommand 之间的编解码。NettyServerHandler 拿到解码得到的 RemotingCommand 后，根据 RemotingCommand.type 来判断是 request 还是 response来进行相应处理，根据业务请求码封装成不同的task任务后，提交给对应的业务processor处理线程池处理。\n\n@Override\npublic\nvoid\nstart() {\n//默认的处理线程池组,使用默认的处理线程池组用于处理后面的多个Netty Handler的逻辑操作\nthis\n.defaultEventExecutorGroup =\nnew\nDefaultEventExecutorGroup\n(\nnettyServerConfig.getServerWorkerThreads(),\nnew\nThreadFactory\n() {\nprivate\nAtomicInteger\nthreadIndex =\nnew\nAtomicInteger\n(\n0\n);\n@Override\npublic\nThread\nnewThread(\nRunnable\nr) {\nreturn\nnew\nThread\n(r,\n\"NettyServerCodecThread_\"\n+\nthis\n.threadIndex.incrementAndGet());\n}\n});\n/**\n* 首先来看下 RocketMQ NettyServer 的 Reactor 线程模型，\n* 一个 Reactor 主线程负责监听 TCP 连接请求;\n* 建立好连接后丢给 Reactor 线程池，它负责将建立好连接的 socket 注册到 selector\n* 上去（这里有两种方式，NIO和Epoll，可配置），然后监听真正的网络数据;\n* 拿到网络数据后，再丢给 Worker 线程池;\n*\n*/\n//RocketMQ-> Java NIO的1+N+M模型：1个acceptor线程，N个IO线程，M1个worker 线程。\nServerBootstrap\nchildHandler =\nthis\n.serverBootstrap.\ngroup\n(\nthis\n.eventLoopGroupBoss,\nthis\n.eventLoopGroupSelector)\n.channel(useEpoll() ?\nEpollServerSocketChannel\n.\nclass\n:\nNioServerSocketChannel\n.\nclass\n)\n.option(\nChannelOption\n.SO_BACKLOG,\n1024\n)\n//服务端处理客户端连接请求是顺序处理的，所以同一时间只能处理一个客户端连接，多个客户端来的时候，服务端将不能处理的客户端连接请求放在队列中等待处理，backlog参数指定了队列的大小\n.option(\nChannelOption\n.SO_REUSEADDR,\ntrue\n)\n//这个参数表示允许重复使用本地地址和端口\n.option(\nChannelOption\n.SO_KEEPALIVE,\nfalse\n)\n//当设置该选项以后，如果在两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文。\n.childOption(\nChannelOption\n.TCP_NODELAY,\ntrue\n)\n//该参数的作用就是禁止使用Nagle算法，使用于小数据即时传输\n.childOption(\nChannelOption\n.SO_SNDBUF, nettyServerConfig.getServerSocketSndBufSize())\n//这两个参数用于操作接收缓冲区和发送缓冲区\n.childOption(\nChannelOption\n.SO_RCVBUF, nettyServerConfig.getServerSocketRcvBufSize())\n.localAddress(\nnew\nInetSocketAddress\n(\nthis\n.nettyServerConfig.getListenPort()))\n.childHandler(\nnew\nChannelInitializer\n<\nSocketChannel\n>() {\n@Override\npublic\nvoid\ninitChannel(\nSocketChannel\nch)\nthrows\nException\n{\nch.pipeline()\n.addLast(defaultEventExecutorGroup, HANDSHAKE_HANDLER_NAME,\nnew\nHandshakeHandler\n(\nTlsSystemConfig\n.tlsMode))\n.addLast(defaultEventExecutorGroup,\nnew\nNettyEncoder\n(),\n//rocketmq解码器,他们分别覆盖了父类的encode和decode方法\nnew\nNettyDecoder\n(),\n//rocketmq编码器\nnew\nIdleStateHandler\n(\n0\n,\n0\n, nettyServerConfig.getServerChannelMaxIdleTimeSeconds()),\n//Netty自带的心跳管理器\nnew\nNettyConnectManageHandler\n(),\n//连接管理器，他负责捕获新连接、连接断开、异常等事件，然后统一调度到NettyEventExecuter处理器处理。\nnew\nNettyServerHandler\n()\n//当一个消息经过前面的解码等步骤后，然后调度到channelRead0方法，然后根据消息类型进行分发\n);\n}\n});\nif\n(nettyServerConfig.isServerPooledByteBufAllocatorEnable()) {\nchildHandler.childOption(\nChannelOption\n.ALLOCATOR,\nPooledByteBufAllocator\n.DEFAULT);\n}\ntry\n{\nChannelFuture\nsync =\nthis\n.serverBootstrap.bind().sync();\nInetSocketAddress\naddr = (\nInetSocketAddress\n) sync.channel().localAddress();\nthis\n.port = addr.getPort();\n}\ncatch\n(\nInterruptedException\ne1) {\nthrow\nnew\nRuntimeException\n(\n\"this.serverBootstrap.bind().sync() InterruptedException\"\n, e1);\n}\nif\n(\nthis\n.channelEventListener !=\nnull\n) {\nthis\n.nettyEventExecutor.start();\n}\n//定时扫描responseTable,获取返回结果,并且处理超时\nthis\n.timer.scheduleAtFixedRate(\nnew\nTimerTask\n() {\n@Override\npublic\nvoid\nrun() {\ntry\n{\nNettyRemotingServer\n.\nthis\n.scanResponseTable();\n}\ncatch\n(\nThrowable\ne) {\nlog.error(\n\"scanResponseTable exception\"\n, e);\n}\n}\n},\n1000\n*\n3\n,\n1000\n);\n}\n\n从上面的描述中可以概括得出RocketMQ的RPC通信部分的Reactor线程池模型框图。\n\n整体可以看出RocketMQ的RPC通信借助Netty的多线程模型，其服务端监听线程和IO线程分离，同时将RPC通信层的业务逻辑与处理具体业务的线程进一步相分离。时间可控的简单业务都直接放在RPC通信部分来完成，复杂和时间不可控的业务提交至后端业务线程池中处理，这样提高了通信效率和MQ整体的性能。（ps：其中抽象出NioEventLoop来表示一个不断循环执行处理任务的线程，每个NioEventLoop有一个selector，用于监听绑定在其上的socket链路。）\n\n\n\n\n\n\n\n\n\n\n', 'RocketMQ的RPC通信', '1', 'Coriger', '2018-08-02 00:01:45', null, '392');
INSERT INTO `article` VALUES ('257285', '10001', '大型互联网公司是怎么处理mysql事务以及隔离级别', '大型互联网公司是怎么处理mysql事务以及隔离级别\n\n1. 简介\nMySQL 事务主要用于处理操作量大，复杂度高的数据。比如说，在人员管理系统中，你删除一个人员，你即需要删除人员的基本资料，也要删除和该人员相关的信息，如信箱，文章等等，这样，这些数据库操作语句就构成一个事务！\n\n在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。\n\n事务处理可以用来维护数据库的完整性，保证成批的 SQL 语句要么全部执行，要么全部不执行。\n\n事务用来管理 insert,update,delete 语句\n\n2. 事务的基本要素ACID\n一般来说，事务是必须满足4个条件（ACID）：：原子性（Atomicity，或称不可分割性）、一致性（Consistency）、隔离性（Isolation，又称独立性）、持久性（Durability）。\n\n原子性：一个事务（transaction）中的所有操作，要么全部完成，要么全部不完成，不会结束在中间某个环节。事务在执行过程中发生错误，会被回滚（Rollback）到事务开始前的状态，就像这个事务从来没有执行过一样。\n\n一致性：在事务开始之前和事务结束以后，数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则，这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。比如A向B转账，不可能A扣了钱，B却没收到。\n\n隔离性：数据库允许多个并发事务同时对其数据进行读写和修改的能力，隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别，包括读未提交（Read uncommitted）、读提交（read committed）、可重复读（repeatable read）和串行化（Serializable）。\n\n持久性：事务完成后，事务对数据库的所有更新将被保存到数据库，不能回滚。\n\n3. 事务的并发问题\n脏读：允许读取未提交的脏数据，比如：事务A读取了事务B更新的数据，然后B回滚操作，那么A读取到的数据是脏数据；\n\n不可重复读：如果你在时间点t1读取了一些记录，在t2时间点也想重新读取一样的数据时，这些记录可能已经被改变，或者消失，比如：事务 A 多次读取同一数据，事务 B 在事务A多次读取的过程中，对数据作了更新并提交，导致事务A多次读取同一数据时，结果不一致。\n\n幻读：系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级，但是系统管理员B就在这个时候插入了一条具体分数的记录，当系统管理员A改结束后发现还有一条记录没有改过来，就好像发生了幻觉一样，这就叫幻读。\n\n不可重复读的和幻读很容易混淆，不可重复读侧重于修改，幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行，解决幻读需要锁表。\n\n4. 事务的4种隔离级别\n为了解决上面事务的并发问题，sql标准提出了4种隔离级别，下面是每种隔离级别能够解决的问题对应关系：\n\n事务隔离级别	脏读	不可重复读	幻读\nread-uncommitted	N	N	N\nread-committed	Y	N	N\nrepeatable-read(default)	Y	Y	N\nserializable	Y	Y	Y\nmysql的默认隔离级别是Repeatable。\n\n查看系统级和会话级的隔离级别：\n\nmysql> select @@global.tx_isolation,@@tx_isolation;+-----------------------+-----------------+| @@global.tx_isolation | @@tx_isolation |+-----------------------+-----------------+| REPEATABLE-READ | REPEATABLE-READ |+-----------------------+-----------------+1 row in set, 2 warnings (0.01 sec)\n下面用例子说明一下这四种隔离级别：\n\n1. read-uncommitted\n\n更改隔离级别为read-uncommitted：\n\nmysql> set session tx_isolation=\'read-uncommitted\';Query OK, 0 rows affected, 1 warning (0.01 sec)mysql> select @@tx_isolation;+------------------+| @@tx_isolation |+------------------+| READ-UNCOMMITTED |+------------------+1 row in set, 1 warning (0.00 sec)\n首先，准备一些测试数据：\n\nmysql> select * from user;+----+----------+------+| id | name | age |+----+----------+------+| 1 | zhangsan | 25 || 2 | lisi | 26 || 3 | wangwu | 27 || 4 | nike | 28 || 5 | lucy | 29 |+----+----------+------+5 rows in set (0.00 sec)\n客户端A：\n\nmysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> select * from user;+----+----------+------+| id | name | age |+----+----------+------+| 1 | zhangsan | 25 || 2 | lisi | 26 || 3 | wangwu | 27 || 4 | nike | 28 || 5 | lucy | 29 |+----+----------+------+5 rows in set (0.00 sec)\n客户端B：\n\nmysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> update user set age=52 where name=\'zhangsan\';Query OK, 1 row affected (0.00 sec)Rows matched: 1 Changed: 1 Warnings: 0\n客户端A：\n\nmysql> select * from user;+----+----------+------+| id | name | age |+----+----------+------+| 1 | zhangsan | 52 || 2 | lisi | 26 || 3 | wangwu | 27 || 4 | nike | 28 || 5 | lucy | 29 |+----+----------+------+5 rows in set (0.00 sec)\n可以看到，客户端B的事务还没有提交，在客户端A的事务内就看到了更新的数据。\n\n客户端B：\n\nmysql> rollback;Query OK, 0 rows affected (0.02 sec)\n客户端A：\n\nmysql> select * from user;+----+----------+------+| id | name | age |+----+----------+------+| 1 | zhangsan | 25 || 2 | lisi | 26 || 3 | wangwu | 27 || 4 | nike | 28 || 5 | lucy | 29 |+----+----------+------+5 rows in set (0.00 sec)\n由于客户端B的事务回滚，客户端A读取的数据又变成了原始数据，因此上一次客户端A读取的数据变成了脏数据。在并发事务中，这种读取数据的问题就叫做脏读。\n\n2. read-commited\n\n要解决上面的问题，可以把数据库的隔离级别改成read-commited。\n\n客户端A：\n\nmysql> set session tx_isolation=\'read-committed\';Query OK, 0 rows affected, 1 warning (0.00 sec)\n再按照上述步骤测试一下，发现脏读问题已经解决，在事务B没有commit之前，事务A不会读取到脏数据。\n\n下面演示一下不可重复读的问题。\n\n客户端A：\n\nmysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> select * from user;+----+----------+------+| id | name | age |+----+----------+------+| 1 | zhangsan | 25 || 2 | lisi | 26 || 3 | wangwu | 27 || 4 | nike | 28 || 5 | lucy | 29 |+----+----------+------+5 rows in set (0.00 sec)\n客户端B：\n\nmysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> update user set age=52 where name=\'zhangsan\';Query OK, 1 row affected (0.01 sec)Rows matched: 1 Changed: 1 Warnings: 0mysql> commit;Query OK, 0 rows affected (0.01 sec)\n客户端A：\n\nmysql> select * from user;+----+----------+------+| id | name | age |+----+----------+------+| 1 | zhangsan | 52 || 2 | lisi | 26 || 3 | wangwu | 27 || 4 | nike | 28 || 5 | lucy | 29 |+----+----------+------+5 rows in set (0.00 sec)mysql> rollback;Query OK, 0 rows affected (0.00 sec)\n可以看到在客户端B的事务提交前后，客户端A读取到的数据不一样了。也就是重复读取相同的数据有不同的结果。\n\n个人理解，脏读也属于不可重复读的一个范畴，只是脏读在事务B未提交之前就导致两次读取数据不一样，不可重复读在事务B提交之后导致两次读取结果不一样。还有就是脏读之所以叫脏数据，是因为这条数据没有真正的在数据库中保存过，这是事务的一个中间状态。而不可重复读两次读取不同的数据实际都已经存在于数据库中了。\n\n3. repeatable-read\n\n要解决不可重复读的问题，可以将数据库的隔离级别改为repeatable-read。\n\n客户端A：\n\nmysql> set session tx_isolation=\'repeatable-read\';Query OK, 0 rows affected, 1 warning (0.00 sec)mysql> select @@tx_isolation;+-----------------+| @@tx_isolation |+-----------------+| REPEATABLE-READ |+-----------------+1 row in set, 1 warning (0.00 sec)\n再按照上述步骤测试一下，发现不可重复读的问题已经解决，在事务B没有commit之后，事务A读取的数据没有变化，关闭这个事务重新打开一个事务才会读到更新后的数据。\n\n下面演示一下幻读的问题。\n\n客户端A：\n\nmysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> select * from user;+----+----------+------+| id | name | age |+----+----------+------+| 1 | zhangsan | 25 || 2 | lisi | 26 || 3 | wangwu | 27 || 4 | nike | 28 || 5 | lucy | 29 |+----+----------+------+5 rows in set (0.00 sec)\n客户端B：\n\nmysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> insert into user values(6,\'shell\',30);Query OK, 1 row affected (0.01 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)\n客户端A：\n\nmysql> insert into user values(6,\'shell\',30);ERROR 1062 (23000): Duplicate entry \'6\' for key \'PRIMARY\'mysql> select * from user;+----+----------+------+| id | name | age |+----+----------+------+| 1 | zhangsan | 25 || 2 | lisi | 26 || 3 | wangwu | 27 || 4 | nike | 28 || 5 | lucy | 29 |+----+----------+------+5 rows in set (0.00 sec)\n可以看到，对于客户端A来说，命名没有id为6的数据，但是还是插入失败，再查询一下还是没有啊，感觉产生了幻觉，这就是幻读问题。幻读和不可重复读的区别在于，不可重复读重点是更新后的读取，幻读重点是插入删除这些操作，解决不可重复读，只需要对对应的数据行加锁就行了。解决幻读则需要对整张表加锁。\n\n如果两个事务B没有提交之前事务A执行插入会如何呢？我们来看一下：\n\n客户端A：\n\nmysql> insert into user values(6,\'shell\',30);\n可以看到如果插入的id和事务B一样，那么事务A的操作会被阻塞，直到事务B提交commit后，才会报错：\n\n客户端A：\n\nmysql> insert into user values(8,\'svn\',32);ERROR 1062 (23000): Duplicate entry \'8\' for key \'PRIMARY\'\n如果客户端A插入到的数据事务B不冲突，那么会立即返回成功：\n\n客户端A：\n\nmysql> insert into user values(9,\'svn\',32);Query OK, 1 row affected (0.00 sec)\n4. serializable\n\n要解决幻读的问题，可以将数据库的隔离级别改为serializable。\n\n客户端A：\n\nmysql> set session tx_isolation=\'serializable\';Query OK, 0 rows affected, 1 warning (0.00 sec)\n再按照上述步骤测试一下，发现幻读的问题已经解决，当事务B尝试insert的事务，被阻塞，也就是事务A将整张表锁住了。直到事务A提交commit以后，事务B的操作才会返回结果。\n\n在这种情况下，只允许一个事务在执行，其它事务必须等待这个事务执行完后才能执行。没有并发，只是单纯的串行。\n\n5. 总结\nmysql中默认事务隔离级别是可重复读时并不会锁住读取到的行;\n\n事务隔离级别为读提交时，写数据只会锁住相应的行;\n\n事务隔离级别为可重复读时，如果有索引（包括主键索引）的时候，以索引列为条件更新数据，会存在间隙锁间隙锁、行锁、下一键锁的问题，从而锁住一些行；如果没有索引，更新数据时会锁住整张表;\n\n事务隔离级别为串行化时，读写数据都会锁住整张表;\n\n隔离级别越高，越能保证数据的完整性和一致性，但是对并发性能的影响也越大，鱼和熊掌不可兼得啊。对于多数应用程序，可以优先考虑把数据库系统的隔离级别设为Read Committed，它能够避免脏读取，而且具有较好的并发性能。尽管它会导致不可重复读、幻读这些并发问题，在可能出现这类问题的个别场合，可以由应用程序采用悲观锁或乐观锁来控制。', '大型互联网公司是怎么处理mysql事务以及隔离级别', '1', 'Coriger', '2018-05-21 22:55:23', null, '176'), ('257905', '10000', '共享阿里云存储OSS', 'OSS外网域名: jiangbin.oss-cn-shanghai.aliyuncs.com', '共享阿里云存储OSS', '0', 'Coriger', '2017-07-09 20:04:08', '2017-07-11 16:45:33', '175'), ('258061', '10000', '阿里一面', '1、[osi七层网络模型，五层网络模型，每次层分别有哪些协议:](https://www.cnblogs.com/wxd0108/p/7597216.html \"osi七层网络模型，五层网络模型，每次层分别有哪些协议:\")\n[![](http://hi.csdn.net/attachment/201201/5/0_1325744597WM32.gif)](http://hi.csdn.net/attachment/201201/5/0_1325744597WM32.gif)\n\n2、[死锁产生的条件， 以及如何避免死锁，银行家算法，产生死锁后如何解决](https://blog.csdn.net/someday1314/article/details/68938841 \"死锁产生的条件， 以及如何避免死锁，银行家算法，产生死锁后如何解决\")\n\n3、[如何判断链表有环](https://www.cnblogs.com/dancingrain/p/3405197.html \"如何判断链表有环\")\n\n4、[虚拟机类加载机制，双亲委派模型，以及为什么要实现双亲委派模型](https://blog.csdn.net/xu768840497/article/details/79175335 \"虚拟机类加载机制，双亲委派模型，以及为什么要实现双亲委派模型\")\nhttps://blog.csdn.net/h2604396739/article/details/78115552\n\n5、[虚拟机调优参数](https://blog.csdn.net/u013891584/article/details/81113581 \"虚拟机调优参数\")\nhttps://www.cnblogs.com/cxzdy/p/5388509.html\n\n6、[拆箱装箱的原理](https://blog.csdn.net/hp910315/article/details/48654777 \"拆箱装箱的原理\")\nhttps://blog.csdn.net/JairusChan/article/details/7513045\nhttps://www.cnblogs.com/dolphin0520/p/3780005.html\n\n7、[JVM垃圾回收算法](https://www.cnblogs.com/aspirant/p/8662690.html \"JVM垃圾回收算法\")\nhttps://www.cnblogs.com/cielosun/p/6674431.html\n\n8、[CMS G1](https://www.cnblogs.com/rgever/p/9534857.html \"CMS G1\")\nhttps://blog.csdn.net/u011546953/article/details/78994882\n\n9、[hashset和hashmap的区别，haspmap的底层实现put操作，扩容机制，currenthashmap如何解决线程安全,1.7版本以及1.8版本的不同](https://blog.csdn.net/u013030086/article/details/84976777 \"hashset和hashmap的区别，haspmap的底层实现put操作，扩容机制，currenthashmap如何解决线程安全,1.7版本以及1.8版本的不同\")\nhttps://www.cnblogs.com/javabg/p/7258550.html\nhttp://www.cnblogs.com/yanzige/p/8392142.html\nhttps://www.cnblogs.com/lijiasnong/p/9963808.html\nhttps://blog.csdn.net/y277an/article/details/77696612\nhttps://blog.csdn.net/jjc120074203/article/details/78625433\nhttps://blog.csdn.net/laokerr/article/details/79394574\n\n10、[md5加密的原理](https://www.cnblogs.com/hjgods/p/3998570.html \"md5加密的原理\")\nhttps://blog.csdn.net/u012611878/article/details/54000607\n\n11、[有多少种方法可以让线程阻塞，能说多少说多少](https://blog.csdn.net/wk1134314305/article/details/74094406 \"有多少种方法可以让线程阻塞，能说多少说多少\")\n\n12、[synchronized和reetrantlock锁](https://www.cnblogs.com/cxzdgs/p/5746895.html \"synchronized和reetrantlock锁\")\nhttps://blog.csdn.net/zheng548/article/details/54426947\nhttps://www.cnblogs.com/fhhk/p/7403307.html\n\n13、[AQS同步器框架，countdowmlatch，cyclebarrier，semaphore，读写锁](https://www.cnblogs.com/yuandluck/p/9477781.html \"AQS同步器框架，countdowmlatch，cyclebarrier，semaphore，读写锁\")\nhttps://www.cnblogs.com/dolphin0520/p/3920397.html\nhttps://blog.csdn.net/madman188/article/details/51427338\n', '阿里一面', '1', 'Coriger', '2019-01-17 15:12:08', null, '122'), ('259115', '10001', '主流Java数据库连接池分析', '主流Java数据库连接池分析(C3P0,DBCP,TomcatPool,BoneCP,Druid)\n\n常用的主流开源数据库连接池有C3P0、DBCP、Tomcat Jdbc Pool、BoneCP、Druid等\n\nC3p0: 开源的JDBC连接池，实现了数据源和JNDI绑定，支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate、Spring等。单线程，性能较差，适用于小型系统，代码600KB左右。\n\nDBCP (Database Connection Pool):由Apache开发的一个Java数据库连接池项目， Jakarta commons-pool对象池机制，Tomcat使用的连接池组件就是DBCP。单独使用dbcp需要3个包：common-dbcp.jar,common-pool.jar,common-collections.jar，预先将数据库连接放在内存中，应用程序需要建立数据库连接时直接到连接池中申请一个就行，用完再放回。单线程，并发量低，性能不好，适用于小型系统。\n\nTomcat Jdbc Pool：Tomcat在7.0以前都是使用common-dbcp做为连接池组件，但是dbcp是单线程，为保证线程安全会锁整个连接池，性能较差，dbcp有超过60个类，也相对复杂。Tomcat从7.0开始引入了新增连接池模块叫做Tomcat jdbc pool，基于Tomcat JULI，使用Tomcat日志框架，完全兼容dbcp，通过异步方式获取连接，支持高并发应用环境，超级简单核心文件只有8个，支持JMX，支持XA Connection。\n\nBoneCP：官方说法BoneCP是一个高效、免费、开源的Java数据库连接池实现库。设计初衷就是为了提高数据库连接池性能，根据某些测试数据显示，BoneCP的速度是最快的，要比当时第二快速的连接池快25倍左右，完美集成到一些持久化产品如Hibernate和DataNucleus中。BoneCP特色：高度可扩展，快速；连接状态切换的回调机制；允许直接访问连接；自动化重置能力；JMX支持；懒加载能力；支持XML和属性文件配置方式；较好的Java代码组织，100%单元测试分支代码覆盖率；代码40KB左右。\n\nDruid：Druid是Java语言中最好的数据库连接池，Druid能够提供强大的监控和扩展功能，是一个可用于大数据实时查询和分析的高容错、高性能的开源分布式系统，尤其是当发生代码部署、机器故障以及其他产品系统遇到宕机等情况时，Druid仍能够保持100%正常运行。主要特色：为分析监控设计；快速的交互式查询；高可用；可扩展；Druid是一个开源项目，源码托管在github上。\n\n主流连接池各项功能对比如下：\n\n[![主流连接池各项功能对比](http://p3.pstatp.com/large/pgc-image/15253973701396453951d18 \"主流连接池各项功能对比\")](http://p3.pstatp.com/large/pgc-image/15253973701396453951d18 \"主流连接池各项功能对比\")\n\n我们再看一组有HikariCP的\n\n[![HikariCP性能分析](http://p1.pstatp.com/large/pgc-image/1525397370184283b378403 \"HikariCP性能分析\")](http://p1.pstatp.com/large/pgc-image/1525397370184283b378403 \"HikariCP性能分析\")\n\nHikariCP性能分析：\n\nHikariCP通过优化(concurrentBag，fastStatementList )集合来提高并发的读写效率。\n\nHikariCP使用threadlocal缓存连接及大量使用CAS的机制，最大限度的避免lock。单可能带来cpu使用率的上升。\n\n从字节码的维度优化代码。 (default inline threshold for a JVM running the server Hotspot compiler is 35 bytecodes ）让方法尽量在35个字节码一下，来提升jvm的处理效率。\n\nHikariCP做的优化补充如下：\n[![HikariCP做的优化](http://p1.pstatp.com/large/pgc-image/1525397370208426d3d2a01 \"HikariCP做的优化\")](http://p1.pstatp.com/large/pgc-image/1525397370208426d3d2a01 \"HikariCP做的优化\")\n[![1](http://p1.pstatp.com/large/pgc-image/1525397370148ba3bbcb3e0 \"1\")](http://p1.pstatp.com/large/pgc-image/1525397370148ba3bbcb3e0 \"1\")\nmysql connecter 源码里用的就是ping命令\n\n[![mysql connecter 源码里用的就是ping命令](http://p3.pstatp.com/large/pgc-image/152539737014089ea6a21a2 \"mysql connecter 源码里用的就是ping命令\")](http://p3.pstatp.com/large/pgc-image/152539737014089ea6a21a2 \"mysql connecter 源码里用的就是ping命令\")\n比HikariCP更快的数据库连接池\n\n一个同事告诉我，比hikari还快的连接池他也用过、研究过 https://github.com/mauricio/postgresql-async 这是scala生态圈的东西。用netty实现了mysql协议，没用mysql官方的connector，纯异步的，它的连接池是写的比较随便，但是性能依然很好。\n\n前瞻，未来到底是HikariCP还是Druid的天下？\n\n很多人都在问，站在巨人肩膀上的第二代连接池HikariCP和druid到底孰强孰弱？其实我觉得这是一个不必讨论的问题。\n\n我们先来看看未来的趋势：单机的操作系统将会被抛弃，取而代之的是容器调度加编排的云操作系统。裸机或者虚拟机的运行时也将会被容器取代。通信方面将会使用Service Mesh。\n\n也就是说中间件最后的趋势一定是弱化到无感知，这才是最终的一个大道至简的方向。那些maven依赖问题，把二方库写在pom里，监控等代码的硬编码进应用里都将逐渐弱化到不复存在，取而代之的那些java agent（如pinpoint、skywalking之类），抑或是service mesh这种side car模式都是可以做中间件（包括连接池）的监控的。\n\n一个有赞的朋友告诉我，在有赞核心应用，用HikariCP替换durid后，RT出现断崖式下滑（1.5ms ～ 1.2ms） 并且持续稳定毛刺少。性能测试与压测之后，一核心系统与druid相比，性能提高一倍左右。\n\n阿飞做了如下统计工作，都是基于最新tag统计的，只统计java文件和xml文件，druid(alibaba-druid)总行数:430289，HikariCP(brettwooldridge-HikariCP)总行数:18372。 只统计java代码，druid(alibaba-druid)总行数:428749，HikariCP(brettwooldridge-HikariCP)总行数:17556。 再过滤一下test目录，(alibaba-druid)总行数:215232，(brettwooldridge-HikariCP)总行数:7960。 光一个DruidDataSource就3000行，且不说性能，druid是在jdbc的基础上，自己编码做得增强。\n\n如果这么说，druid准确的说是生活在第一代和第二代连接池的面向过程的年代。druid可能忘了松耦合这个概念，把监控和数据库连接池做在一个项目里，本身就是紧耦合。既然微服务提倡业务隔离性，那么这种难道不应该隔离么？让组件工具一次只做一件事不好么？监控的事情在service mesh的将来毕竟是有别的其天然的监控手法的而不是硬编码在一个小小的连接池里。综上所述，放在现在或是未来的趋势去拼，大概率比不过拥抱springboot 2.0以及大道至简精简到极致的HikariCP。\n\n未来的中间件，一定是和spring生态圈和servich mesh一样，大道至简，越来越薄，升级中间件不再是需要用户强行升级maven依赖解决依赖冲突，而是通过mesh的方式极致到升级让业务方无感知。所以那些热部署、潘多拉boot、容器隔离等解决依赖冲突的妥协方式也将可能大概率被置换掉。\n\n从Sharding-jdbc架构演进看未来\n\nDatabase Mesh，一个搭乘 Service Mesh 浪潮衍生出来的新兴词汇。顾名思义，Database Mesh 使用一个啮合层，将散落在系统各个角落中的数据库统一治理起来。通过啮合层集中在一起的应用与数据库之间的交互网络，就像蜘蛛网一样复杂而有序。它的首要目标并非啮合存储于数据库中的数据，而是啮合应用与数据库间的交互。\n\nDatabase Mesh 的关注重点在于如何将分布式的数据访问应用与数据库有机串联起来，它更加关注的是交互，是将杂乱无章的应用与数据库之间的交互有效的梳理。\n\n使用 Database Mesh，访问数据库的应用和数据库终将形成一个巨大的网格体系，应用和数据库只需在网格体系中对号入座即可，它们都是被啮合层所治理的对象。\n\nSharding-JDBC 一直以来，以 JDBC 层分片作为其核心理念。它的架构图如下：\n\n[![架构图](http://p3.pstatp.com/large/pgc-image/1525397370496039521200b \"架构图\")](http://p3.pstatp.com/large/pgc-image/1525397370496039521200b \"架构图\")\n\nSharding-JDBC 将分别实现 Driver、Server 以及 Sidecar 这三个不同的版本，一起组成 Sharding-JDBC 的生态圈，为不同的需求与环境提供更加具有针对性的差异化服务。\n\n[![2](http://p3.pstatp.com/large/pgc-image/1525397370636ff20efa755 \"2\")](http://p3.pstatp.com/large/pgc-image/1525397370636ff20efa755 \"2\")\n\n由于 Sharding-JDBC-Server 的出现，使得原来 DBA 通过 Sharding-JDBC-Driver 无法对数据进行操作的缺憾得到了补偿。由于 Sharding-JDBC-Driver 无需通过代理层进行二次转发，因此线上性能更佳，可以通过以下的混合部署方案使用 Sharding-JDBC:\n\n[![3](http://p3.pstatp.com/large/pgc-image/1525397370520d285ba2df1 \"3\")](http://p3.pstatp.com/large/pgc-image/1525397370520d285ba2df1 \"3\")\n\n线上应用使用 Sharding-JDBC-Driver 直连数据库以获取最优性能，使用 MySQL 命令行或 UI 客户端连接 Sharding-JDBC-Server 方便的查询数据和执行各种 DDL 语句。它们使用同一个注册中心集群，通过管理端配置注册中心中的数据，即可由注册中心自动将配置变更推送至 Driver 和 Server 应用。若数据库拆分的过多而导致连接数会暴涨，则可以考虑直接在线上使用 Sharding-JDBC-Server，以达到有效控制连接数的目的。\n\n在不久的将来，Sharding-JDBC-Sidecar 也将问世，它的部署架构是这样的：\n\n[![4](http://p3.pstatp.com/large/pgc-image/152539737054274acefbdec \"4\")](http://p3.pstatp.com/large/pgc-image/152539737054274acefbdec \"4\")\n\n基于 Sharding-JDBC 的 Database Mesh 与 Service Mesh 互不干扰，相得益彰。服务之间的交互由 Service Mesh Sidecar 接管，基于 SQL 的数据库访问由 Sharding-JDBC-Sidecar 接管。\n\n对于业务应用来说，无论是 RPC 还是对数据库的访问，都无需关注其真实的物理部署结构，做到真正的零侵入。由于 Sharding-JDBC-Sidecar 是随着宿主机的生命周期创建和消亡的，\n\n因此，它并非静态 IP，而是完全动态和弹性的存在，整个系统中并无任何中心节点的存在。对于数据运维等操作，仍然可以通过启动一个 Sharding-JDBC-Server 的进程作为静态 IP 的入口，通过各种命令行或 UI 客户端进行操作。', '主流Java数据库连接池分析', '1', 'Coriger', '2018-05-21 23:11:34', null, '159');
COMMIT;

-- ----------------------------
--  Table structure for `articletag`
-- ----------------------------
DROP TABLE IF EXISTS `articletag`;
CREATE TABLE `articletag` (
  `articleId` int(11) NOT NULL COMMENT '文章Id',
  `tagId` int(11) NOT NULL COMMENT '标签Id',
  `tagName` varchar(50) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='文章标签中间表';

-- ----------------------------
--  Records of `articletag`
-- ----------------------------
BEGIN;
INSERT INTO `articletag` VALUES ('255544', '9999', 'default'), ('255897', '9999', 'default'), ('251827', '9999', 'default'), ('257905', '9999', 'default'), ('252981', '9999', 'default'), ('251655', '9999', 'default'), ('251739', '9999', 'default'), ('257285', '9999', 'default'), ('255784', '9999', 'default'), ('259115', '9999', 'default'), ('251790', '9999', 'default'), ('255836', '9999', 'default'), ('256527', '9999', 'default'), ('253104', '9999', 'default'), ('253831', '9999', 'default'), ('256166', '9999', 'default'), ('258061', '9999', 'default'), ('252036', '9999', 'default'), ('254874', '9999', 'default');
COMMIT;

-- ----------------------------
--  Table structure for `category`
-- ----------------------------
DROP TABLE IF EXISTS `category`;
CREATE TABLE `category` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `categoryName` varchar(20) NOT NULL COMMENT '分类名称  唯一',
  `aliasName` varchar(20) NOT NULL COMMENT '别名  唯一  比如新闻 就用News 代替  栏目Id不显示在url中',
  `sort` int(11) NOT NULL DEFAULT '0' COMMENT '排序 （0-10）',
  PRIMARY KEY (`id`),
  UNIQUE KEY `aliasName_UNIQUE` (`aliasName`),
  UNIQUE KEY `categoryName_UNIQUE` (`categoryName`)
) ENGINE=InnoDB AUTO_INCREMENT=10002 DEFAULT CHARSET=utf8 COMMENT='分类表  只支持一级分类  如果需要分多个层次 用标签来协助实现';

-- ----------------------------
--  Records of `category`
-- ----------------------------
BEGIN;
INSERT INTO `category` VALUES ('9999', 'Blog介绍', 'default', '0'), ('10000', '阿里云', 'aliyun', '0'), ('10001', 'Gingerbread', 'Gingerbread', '0');
COMMIT;

-- ----------------------------
--  Table structure for `log`
-- ----------------------------
DROP TABLE IF EXISTS `log`;
CREATE TABLE `log` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) DEFAULT NULL,
  `url` varchar(1024) DEFAULT NULL,
  `ip` varchar(20) DEFAULT NULL,
  `method` varchar(255) DEFAULT NULL,
  `args` varchar(255) DEFAULT NULL,
  `classMethod` varchar(255) DEFAULT NULL,
  `exception` varchar(2000) DEFAULT NULL,
  `operateTime` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
--  Table structure for `partner`
-- ----------------------------
DROP TABLE IF EXISTS `partner`;
CREATE TABLE `partner` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `siteName` varchar(45) NOT NULL COMMENT '站点名',
  `siteUrl` varchar(45) NOT NULL COMMENT '站点地址',
  `siteDesc` varchar(100) NOT NULL COMMENT '站点描述  简单备注 ',
  `sort` int(11) NOT NULL DEFAULT '0' COMMENT '排序',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='合作伙伴';

-- ----------------------------
--  Records of `partner`
-- ----------------------------
BEGIN;
INSERT INTO `partner` VALUES ('1', 'JB.Gingerbread | github', 'https://github.com/jiangbin216', 'JB.Gingerbread的GitHub', '1'), ('2', 'JB.Gingerbread | 开源中国', 'https://git.oschina.net/jiangbin216/projects', 'JB.Gingerbread的开源中国', '1');
COMMIT;

-- ----------------------------
--  Table structure for `tag`
-- ----------------------------
DROP TABLE IF EXISTS `tag`;
CREATE TABLE `tag` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `tagName` varchar(25) NOT NULL COMMENT '标签名称  唯一',
  `aliasName` varchar(20) NOT NULL COMMENT '标签别名 唯一',
  PRIMARY KEY (`id`),
  UNIQUE KEY `tagName_UNIQUE` (`tagName`)
) ENGINE=InnoDB AUTO_INCREMENT=10000 DEFAULT CHARSET=utf8 COMMENT='标签表';

-- ----------------------------
--  Records of `tag`
-- ----------------------------
BEGIN;
INSERT INTO `tag` VALUES ('9999', 'default', 'default');
COMMIT;

-- ----------------------------
--  Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL,
  `username` varchar(20) DEFAULT NULL,
  `password` varchar(50) DEFAULT NULL,
  `enabled` varchar(5) DEFAULT '0' COMMENT '是否被禁用',
  `credential` varchar(5) DEFAULT '0' COMMENT '凭证是否过期',
  `locked` varchar(5) DEFAULT '0' COMMENT '是否被锁',
  `expired` varchar(5) DEFAULT '0' COMMENT '是否过期',
  `createTime` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `user`
-- ----------------------------
BEGIN;
INSERT INTO `user` VALUES ('1', 'admin', '9FB7A3B5A0C6C35E281ABEB2BE24C34A', 'false', 'false', 'false', 'false', '2017-05-17 14:32:13');
COMMIT;

-- ----------------------------
--  Table structure for `user_info`
-- ----------------------------
DROP TABLE IF EXISTS `user_info`;
CREATE TABLE `user_info` (
  `username` varchar(20) DEFAULT NULL,
  `avatar` varchar(255) DEFAULT NULL,
  `nickname` varchar(20) DEFAULT 'JB.Gingerbread' COMMENT '昵称',
  `phone` char(11) DEFAULT NULL COMMENT '电话号码',
  `email` varchar(50) DEFAULT 'jb.gingerbread@gmail.com' COMMENT '邮箱',
  `signature` varchar(2000) DEFAULT NULL COMMENT '个性签名',
  `address` varchar(50) DEFAULT NULL COMMENT '地址',
  `announcement` varchar(2000) DEFAULT NULL COMMENT '公告',
  `telegram` varchar(20) DEFAULT '0' COMMENT 'telegram账号',
  `wechart` varchar(20) DEFAULT 'kobe_1989' COMMENT '微信账号',
  UNIQUE KEY `user_info_username_uindex` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息表';

-- ----------------------------
--  Records of `user_info`
-- ----------------------------
BEGIN;
INSERT INTO `user_info` VALUES ('admin', 'http://of8rkrh1w.bkt.clouddn.com/2017/6/21/avatar.jpg', 'JB.Gingerbread', '保密', 'jb.gingerbread@gmail.com', '也许我不是最聪明的那个，也许我也不是最努力的那个，也许我也不是最勤奋的那个，但我还在路上，并没有装死。', '中国 - 北京', '个人博客\r\n<br>\r\n                    mail： \r\n             jb.gingerbread@gmail.com<br>\r\n                               \r\n', '0', 'kobe_1989');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;
